Coverage Report

Created: 2025-08-03 06:57

/src/fwupd/libfwupdplugin/fu-device.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright 2015 Richard Hughes <richard@hughsie.com>
3
 *
4
 * SPDX-License-Identifier: LGPL-2.1-or-later
5
 */
6
7
0
#define G_LOG_DOMAIN "FuDevice"
8
9
#include "config.h"
10
11
#include <gio/gio.h>
12
#include <string.h>
13
14
#include "fwupd-common.h"
15
#include "fwupd-device-private.h"
16
#include "fwupd-enums-private.h"
17
18
#include "fu-byte-array.h"
19
#include "fu-bytes.h"
20
#include "fu-chunk-array.h"
21
#include "fu-device-event-private.h"
22
#include "fu-device-private.h"
23
#include "fu-input-stream.h"
24
#include "fu-output-stream.h"
25
#include "fu-quirks.h"
26
#include "fu-security-attr.h"
27
#include "fu-string.h"
28
#include "fu-version-common.h"
29
30
0
#define FU_DEVICE_RETRY_OPEN_COUNT 5
31
0
#define FU_DEVICE_RETRY_OPEN_DELAY 500 /* ms */
32
33
/**
34
 * FuDevice:
35
 *
36
 * A physical or logical device that is exported to the daemon.
37
 *
38
 * See also: [class@FuDeviceLocker], [class@Fwupd.Device]
39
 */
40
41
static void
42
fu_device_finalize(GObject *object);
43
static void
44
fu_device_inhibit_full(FuDevice *self,
45
           FwupdDeviceProblem problem,
46
           const gchar *inhibit_id,
47
           const gchar *reason);
48
49
typedef struct {
50
  gchar *equivalent_id;
51
  gchar *physical_id;
52
  gchar *logical_id;
53
  gchar *backend_id;
54
  gchar *update_request_id;
55
  gchar *update_message;
56
  gchar *update_image;
57
  gchar *fwupd_version;
58
  gchar *proxy_guid;
59
  FuDevice *proxy;    /* noref */
60
  FuDevice *target;   /* ref */
61
  FuBackend *backend; /* noref */
62
  FuContext *ctx;
63
  gint64 created_usec;
64
  gint64 modified_usec;
65
  guint16 vid;
66
  guint16 pid;
67
  GHashTable *inhibits;   /* (nullable) */
68
  GHashTable *metadata;   /* (nullable) */
69
  GPtrArray *parent_guids;  /* (nullable) (element-type utf-8) */
70
  GPtrArray *parent_physical_ids; /* (nullable) */
71
  GPtrArray *parent_backend_ids;  /* (nullable) */
72
  GPtrArray *events;    /* (nullable) (element-type FuDeviceEvent) */
73
  guint event_idx;
74
  guint remove_delay;    /* ms */
75
  guint acquiesce_delay; /* ms */
76
  guint request_cnts[FWUPD_REQUEST_KIND_LAST];
77
  gint order;
78
  guint priority;
79
  guint poll_id;
80
  gint poll_locker_cnt;
81
  gboolean done_probe;
82
  gboolean done_setup;
83
  gboolean device_id_valid;
84
  guint64 size_min;
85
  guint64 size_max;
86
  guint64 required_free; /* bytes */
87
  gint open_refcount;    /* atomic */
88
  GType specialized_gtype;
89
  GType proxy_gtype;
90
  GType firmware_gtype;
91
  GPtrArray *possible_plugins; /* (element-type utf-8) */
92
  GPtrArray *instance_ids;     /* (nullable) (element-type FuDeviceInstanceIdItem) */
93
  GPtrArray *retry_recs;       /* (nullable) (element-type FuDeviceRetryRecovery) */
94
  guint retry_delay;
95
  GArray *private_flags_registered; /* (nullable) (element-type GQuark) */
96
  GArray *private_flags;      /* (nullable) (element-type GQuark) */
97
  gchar *custom_flags;
98
  gulong notify_flags_proxy_id;
99
  GHashTable *instance_hash; /* (nullable) */
100
  FuProgress *progress;    /* provided for FuDevice notify callbacks */
101
} FuDevicePrivate;
102
103
typedef struct {
104
  GQuark domain;
105
  gint code;
106
  FuDeviceRetryFunc recovery_func;
107
} FuDeviceRetryRecovery;
108
109
typedef struct {
110
  FwupdDeviceProblem problem;
111
  gchar *inhibit_id;
112
  gchar *reason;
113
} FuDeviceInhibit;
114
115
typedef struct {
116
  gchar *instance_id;
117
  gchar *guid;
118
  FuDeviceInstanceFlags flags;
119
} FuDeviceInstanceIdItem;
120
121
enum {
122
  PROP_0,
123
  PROP_PHYSICAL_ID,
124
  PROP_LOGICAL_ID,
125
  PROP_BACKEND_ID,
126
  PROP_EQUIVALENT_ID,
127
  PROP_UPDATE_MESSAGE,
128
  PROP_UPDATE_IMAGE,
129
  PROP_CONTEXT,
130
  PROP_BACKEND,
131
  PROP_PROXY,
132
  PROP_PARENT,
133
  PROP_PRIVATE_FLAGS,
134
  PROP_VID,
135
  PROP_PID,
136
  PROP_REQUIRED_FREE,
137
  PROP_LAST
138
};
139
140
enum { SIGNAL_CHILD_ADDED, SIGNAL_CHILD_REMOVED, SIGNAL_REQUEST, SIGNAL_LAST };
141
142
static guint signals[SIGNAL_LAST] = {0};
143
144
/* ordered for query speed; most frequent first */
145
enum {
146
  QUARK_NO_PROBE,
147
  QUARK_REFCOUNTED_PROXY,
148
  QUARK_NO_GENERIC_GUIDS,
149
  QUARK_NO_SERIAL_NUMBER,
150
  QUARK_IS_FAKE,
151
  QUARK_LAST,
152
};
153
154
static guint quarks[QUARK_LAST] = {0};
155
156
G_DEFINE_TYPE_WITH_PRIVATE(FuDevice, fu_device, FWUPD_TYPE_DEVICE);
157
158
0
#define GET_PRIVATE(o) (fu_device_get_instance_private(o))
159
160
static void
161
fu_device_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
162
0
{
163
0
  FuDevice *self = FU_DEVICE(object);
164
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
165
0
  switch (prop_id) {
166
0
  case PROP_PHYSICAL_ID:
167
0
    g_value_set_string(value, priv->physical_id);
168
0
    break;
169
0
  case PROP_LOGICAL_ID:
170
0
    g_value_set_string(value, priv->logical_id);
171
0
    break;
172
0
  case PROP_BACKEND_ID:
173
0
    g_value_set_string(value, priv->backend_id);
174
0
    break;
175
0
  case PROP_EQUIVALENT_ID:
176
0
    g_value_set_string(value, priv->equivalent_id);
177
0
    break;
178
0
  case PROP_UPDATE_MESSAGE:
179
0
    g_value_set_string(value, priv->update_message);
180
0
    break;
181
0
  case PROP_UPDATE_IMAGE:
182
0
    g_value_set_string(value, priv->update_image);
183
0
    break;
184
0
  case PROP_CONTEXT:
185
0
    g_value_set_object(value, priv->ctx);
186
0
    break;
187
0
  case PROP_BACKEND:
188
0
    g_value_set_object(value, priv->backend);
189
0
    break;
190
0
  case PROP_PROXY:
191
0
    g_value_set_object(value, priv->proxy);
192
0
    break;
193
0
  case PROP_PARENT:
194
0
    g_value_set_object(value, fu_device_get_parent(self));
195
0
    break;
196
0
  case PROP_PRIVATE_FLAGS:
197
0
    g_value_set_uint64(value, priv->private_flags->len);
198
0
    break;
199
0
  case PROP_VID:
200
0
    g_value_set_uint(value, priv->vid);
201
0
    break;
202
0
  case PROP_PID:
203
0
    g_value_set_uint(value, priv->pid);
204
0
    break;
205
0
  case PROP_REQUIRED_FREE:
206
0
    g_value_set_uint64(value, priv->required_free);
207
0
    break;
208
0
  default:
209
0
    G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
210
0
    break;
211
0
  }
212
0
}
213
214
static void
215
fu_device_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
216
0
{
217
0
  FuDevice *self = FU_DEVICE(object);
218
0
  switch (prop_id) {
219
0
  case PROP_PHYSICAL_ID:
220
0
    fu_device_set_physical_id(self, g_value_get_string(value));
221
0
    break;
222
0
  case PROP_LOGICAL_ID:
223
0
    fu_device_set_logical_id(self, g_value_get_string(value));
224
0
    break;
225
0
  case PROP_BACKEND_ID:
226
0
    fu_device_set_backend_id(self, g_value_get_string(value));
227
0
    break;
228
0
  case PROP_EQUIVALENT_ID:
229
0
    fu_device_set_equivalent_id(self, g_value_get_string(value));
230
0
    break;
231
0
  case PROP_UPDATE_MESSAGE:
232
0
    fu_device_set_update_message(self, g_value_get_string(value));
233
0
    break;
234
0
  case PROP_UPDATE_IMAGE:
235
0
    fu_device_set_update_image(self, g_value_get_string(value));
236
0
    break;
237
0
  case PROP_CONTEXT:
238
0
    fu_device_set_context(self, g_value_get_object(value));
239
0
    break;
240
0
  case PROP_BACKEND:
241
0
    fu_device_set_backend(self, g_value_get_object(value));
242
0
    break;
243
0
  case PROP_PROXY:
244
0
    fu_device_set_proxy(self, g_value_get_object(value));
245
0
    break;
246
0
  case PROP_PARENT:
247
0
    fu_device_set_parent(self, g_value_get_object(value));
248
0
    break;
249
0
  case PROP_VID:
250
0
    fu_device_set_vid(self, g_value_get_uint(value));
251
0
    break;
252
0
  case PROP_PID:
253
0
    fu_device_set_pid(self, g_value_get_uint(value));
254
0
    break;
255
0
  case PROP_REQUIRED_FREE:
256
0
    fu_device_set_required_free(self, g_value_get_uint64(value));
257
0
    break;
258
0
  default:
259
0
    G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
260
0
    break;
261
0
  }
262
0
}
263
264
static void
265
fu_device_register_private_flags(FuDevice *self)
266
0
{
267
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
268
0
  const gchar *flags[] = {
269
0
      FU_DEVICE_PRIVATE_FLAG_MD_SET_ICON,
270
0
      FU_DEVICE_PRIVATE_FLAG_MD_SET_NAME,
271
0
      FU_DEVICE_PRIVATE_FLAG_MD_SET_NAME_CATEGORY,
272
0
      FU_DEVICE_PRIVATE_FLAG_MD_SET_VERFMT,
273
0
      FU_DEVICE_PRIVATE_FLAG_ONLY_SUPPORTED,
274
0
      FU_DEVICE_PRIVATE_FLAG_NO_AUTO_INSTANCE_IDS,
275
0
      FU_DEVICE_PRIVATE_FLAG_ENSURE_SEMVER,
276
0
      FU_DEVICE_PRIVATE_FLAG_RETRY_OPEN,
277
0
      FU_DEVICE_PRIVATE_FLAG_REPLUG_MATCH_GUID,
278
0
      FU_DEVICE_PRIVATE_FLAG_INHERIT_ACTIVATION,
279
0
      FU_DEVICE_PRIVATE_FLAG_IS_OPEN,
280
0
      FU_DEVICE_PRIVATE_FLAG_AUTO_PARENT_CHILDREN,
281
0
      FU_DEVICE_PRIVATE_FLAG_ATTACH_EXTRA_RESET,
282
0
      FU_DEVICE_PRIVATE_FLAG_INHIBIT_CHILDREN,
283
0
      FU_DEVICE_PRIVATE_FLAG_NO_AUTO_REMOVE_CHILDREN,
284
0
      FU_DEVICE_PRIVATE_FLAG_USE_PARENT_FOR_OPEN,
285
0
      FU_DEVICE_PRIVATE_FLAG_USE_PROXY_FOR_OPEN,
286
0
      FU_DEVICE_PRIVATE_FLAG_USE_PARENT_FOR_BATTERY,
287
0
      FU_DEVICE_PRIVATE_FLAG_USE_PROXY_FALLBACK,
288
0
      FU_DEVICE_PRIVATE_FLAG_NO_AUTO_REMOVE,
289
0
      FU_DEVICE_PRIVATE_FLAG_MD_SET_VENDOR,
290
0
      FU_DEVICE_PRIVATE_FLAG_NO_LID_CLOSED,
291
0
      FU_DEVICE_PRIVATE_FLAG_MD_SET_SIGNED,
292
0
      FU_DEVICE_PRIVATE_FLAG_AUTO_PAUSE_POLLING,
293
0
      FU_DEVICE_PRIVATE_FLAG_DELAYED_REMOVAL,
294
0
      FU_DEVICE_PRIVATE_FLAG_IGNORE_SYSTEM_POWER,
295
0
      FU_DEVICE_PRIVATE_FLAG_SAVE_INTO_BACKUP_REMOTE,
296
0
      FU_DEVICE_PRIVATE_FLAG_MD_SET_FLAGS,
297
0
      FU_DEVICE_PRIVATE_FLAG_MD_SET_VERSION,
298
0
      FU_DEVICE_PRIVATE_FLAG_MD_ONLY_CHECKSUM,
299
0
      FU_DEVICE_PRIVATE_FLAG_ADD_INSTANCE_ID_REV,
300
0
      FU_DEVICE_PRIVATE_FLAG_UNCONNECTED,
301
0
      FU_DEVICE_PRIVATE_FLAG_DISPLAY_REQUIRED,
302
0
      FU_DEVICE_PRIVATE_FLAG_UPDATE_PENDING,
303
0
      FU_DEVICE_PRIVATE_FLAG_ENFORCE_REQUIRES,
304
0
      FU_DEVICE_PRIVATE_FLAG_HOST_FIRMWARE,
305
0
      FU_DEVICE_PRIVATE_FLAG_HOST_FIRMWARE_CHILD,
306
0
      FU_DEVICE_PRIVATE_FLAG_HOST_CPU,
307
0
      FU_DEVICE_PRIVATE_FLAG_HOST_CPU_CHILD,
308
0
      FU_DEVICE_PRIVATE_FLAG_EXPLICIT_ORDER,
309
0
      FU_DEVICE_PRIVATE_FLAG_INSTALL_PARENT_FIRST,
310
0
      FU_DEVICE_PRIVATE_FLAG_REGISTERED,
311
0
      FU_DEVICE_PRIVATE_FLAG_ADD_COUNTERPART_GUIDS,
312
0
      FU_DEVICE_PRIVATE_FLAG_USE_RUNTIME_VERSION,
313
0
      FU_DEVICE_PRIVATE_FLAG_SKIPS_RESTART,
314
0
      FU_DEVICE_PRIVATE_FLAG_COUNTERPART_VISIBLE,
315
0
      FU_DEVICE_PRIVATE_FLAG_DETACH_PREPARE_FIRMWARE,
316
0
      FU_DEVICE_PRIVATE_FLAG_EMULATED_REQUIRE_SETUP,
317
0
      FU_DEVICE_PRIVATE_FLAG_INSTALL_LOOP_RESTART,
318
0
      FU_DEVICE_PRIVATE_FLAG_MD_SET_REQUIRED_FREE,
319
0
  };
320
0
  GQuark quarks_tmp[G_N_ELEMENTS(flags)] = {0};
321
0
  if (G_LIKELY(priv->private_flags_registered->len > 0))
322
0
    return;
323
0
  for (guint i = 0; i < G_N_ELEMENTS(flags); i++)
324
0
    quarks_tmp[i] = g_quark_from_static_string(flags[i]);
325
0
  g_array_append_vals(priv->private_flags_registered, quarks, G_N_ELEMENTS(quarks));
326
0
  g_array_append_vals(priv->private_flags_registered, quarks_tmp, G_N_ELEMENTS(quarks_tmp));
327
0
}
328
329
/* private */
330
gboolean
331
fu_device_has_private_flag_quark(FuDevice *self, GQuark flag_quark)
332
0
{
333
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
334
0
  for (guint i = 0; i < priv->private_flags->len; i++) {
335
0
    GQuark flag_tmp = g_array_index(priv->private_flags, GQuark, i);
336
0
    if (flag_quark == flag_tmp)
337
0
      return TRUE;
338
0
  }
339
0
  return FALSE;
340
0
}
341
342
static gboolean
343
fu_device_private_flags_has_registered_quark(FuDevice *self, GQuark flag_quark)
344
0
{
345
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
346
0
  for (guint i = 0; i < priv->private_flags_registered->len; i++) {
347
0
    GQuark flag_tmp = g_array_index(priv->private_flags_registered, GQuark, i);
348
0
    if (flag_quark == flag_tmp)
349
0
      return TRUE;
350
0
  }
351
0
  return FALSE;
352
0
}
353
354
static gboolean
355
fu_device_add_private_flag_quark(FuDevice *self, GQuark flag_quark)
356
0
{
357
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
358
0
  if (fu_device_has_private_flag_quark(self, flag_quark))
359
0
    return FALSE;
360
0
  g_array_append_val(priv->private_flags, flag_quark);
361
0
  return TRUE;
362
0
}
363
364
static gboolean
365
fu_device_remove_private_flag_quark(FuDevice *self, GQuark flag_quark)
366
0
{
367
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
368
0
  for (guint i = 0; i < priv->private_flags->len; i++) {
369
0
    GQuark flag_tmp = g_array_index(priv->private_flags, GQuark, i);
370
0
    if (flag_quark == flag_tmp) {
371
0
      g_array_remove_index(priv->private_flags, i);
372
0
      return TRUE;
373
0
    }
374
0
  }
375
0
  return FALSE;
376
0
}
377
378
static GQuark
379
fu_device_find_private_flag_quark(FuDevice *self, const gchar *flag)
380
0
{
381
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
382
0
  GQuark flag_quark = g_quark_from_string(flag);
383
0
  for (guint i = 0; i < priv->private_flags_registered->len; i++) {
384
0
    GQuark flag_tmp = g_array_index(priv->private_flags_registered, GQuark, i);
385
0
    if (flag_quark == flag_tmp)
386
0
      return flag_tmp;
387
0
  }
388
0
  return 0;
389
0
}
390
391
/**
392
 * fu_device_add_private_flag:
393
 * @self: a #FuDevice
394
 * @flag: a device flag
395
 *
396
 * Adds a private flag that can be used by the plugin for any purpose.
397
 *
398
 * Since: 1.6.2
399
 **/
400
void
401
fu_device_add_private_flag(FuDevice *self, const gchar *flag)
402
0
{
403
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
404
0
  GQuark flag_quark;
405
406
0
  g_return_if_fail(FU_IS_DEVICE(self));
407
0
  g_return_if_fail(flag != NULL);
408
409
  /* ensure */
410
0
  fu_device_register_private_flags(self);
411
412
  /* do not let devices be updated until re-connected */
413
0
  if (g_strcmp0(flag, FU_DEVICE_PRIVATE_FLAG_UNCONNECTED) == 0)
414
0
    fu_device_inhibit(self, "unconnected", "Device has been removed");
415
416
  /* add counterpart GUIDs already added */
417
0
  if (g_strcmp0(flag, FU_DEVICE_PRIVATE_FLAG_COUNTERPART_VISIBLE) == 0) {
418
0
    for (guint i = 0; priv->instance_ids != NULL && i < priv->instance_ids->len; i++) {
419
0
      FuDeviceInstanceIdItem *item = g_ptr_array_index(priv->instance_ids, i);
420
0
      if (item->flags & FU_DEVICE_INSTANCE_FLAG_COUNTERPART)
421
0
        item->flags |= FU_DEVICE_INSTANCE_FLAG_VISIBLE;
422
0
    }
423
0
  }
424
425
  /* reset this back to the default */
426
0
  if (g_strcmp0(flag, FU_DEVICE_PRIVATE_FLAG_EXPLICIT_ORDER) == 0) {
427
0
    GPtrArray *children = fu_device_get_children(self);
428
0
    for (guint i = 0; i < children->len; i++) {
429
0
      FuDevice *child_tmp = g_ptr_array_index(children, i);
430
0
      fu_device_add_private_flag(child_tmp,
431
0
               FU_DEVICE_PRIVATE_FLAG_EXPLICIT_ORDER);
432
0
    }
433
0
    fu_device_set_order(self, G_MAXINT);
434
0
  }
435
436
  /* check exists */
437
0
  flag_quark = fu_device_find_private_flag_quark(self, flag);
438
0
  if (G_UNLIKELY(flag_quark == 0)) {
439
0
#ifndef SUPPORTED_BUILD
440
0
    g_critical("%s flag %s is unknown -- use fu_device_register_private_flag()",
441
0
         G_OBJECT_TYPE_NAME(self),
442
0
         flag);
443
0
#endif
444
0
    return;
445
0
  }
446
0
  if (fu_device_add_private_flag_quark(self, flag_quark))
447
0
    g_object_notify(G_OBJECT(self), "private-flags");
448
0
}
449
450
/**
451
 * fu_device_remove_private_flag:
452
 * @self: a #FuDevice
453
 * @flag: a device flag
454
 *
455
 * Removes a private flag that can be used by the plugin for any purpose.
456
 *
457
 * Since: 1.6.2
458
 **/
459
void
460
fu_device_remove_private_flag(FuDevice *self, const gchar *flag)
461
0
{
462
0
  GQuark flag_quark;
463
464
0
  g_return_if_fail(FU_IS_DEVICE(self));
465
0
  g_return_if_fail(flag != NULL);
466
467
  /* ensure */
468
0
  fu_device_register_private_flags(self);
469
470
0
  if (g_strcmp0(flag, FU_DEVICE_PRIVATE_FLAG_UNCONNECTED) == 0)
471
0
    fu_device_uninhibit(self, "unconnected");
472
473
0
  flag_quark = fu_device_find_private_flag_quark(self, flag);
474
0
  if (G_UNLIKELY(flag_quark == 0)) {
475
0
#ifndef SUPPORTED_BUILD
476
0
    g_critical("%s flag %s is unknown -- use fu_device_register_private_flag()",
477
0
         G_OBJECT_TYPE_NAME(self),
478
0
         flag);
479
0
#endif
480
0
    return;
481
0
  }
482
0
  if (fu_device_remove_private_flag_quark(self, flag_quark))
483
0
    g_object_notify(G_OBJECT(self), "private-flags");
484
0
}
485
486
/**
487
 * fu_device_has_private_flag:
488
 * @self: a #FuDevice
489
 * @flag: a device flag
490
 *
491
 * Tests for a private flag that can be used by the plugin for any purpose.
492
 *
493
 * Since: 1.6.2
494
 **/
495
gboolean
496
fu_device_has_private_flag(FuDevice *self, const gchar *flag)
497
0
{
498
0
  GQuark flag_quark;
499
500
0
  g_return_val_if_fail(FU_IS_DEVICE(self), FALSE);
501
0
  g_return_val_if_fail(flag != NULL, FALSE);
502
503
  /* ensure */
504
0
  fu_device_register_private_flags(self);
505
506
0
  flag_quark = fu_device_find_private_flag_quark(self, flag);
507
0
  if (G_UNLIKELY(flag_quark == 0)) {
508
0
#ifndef SUPPORTED_BUILD
509
0
    g_critical("%s flag %s is unknown -- use fu_device_register_private_flag()",
510
0
         G_OBJECT_TYPE_NAME(self),
511
0
         flag);
512
0
#endif
513
0
    return FALSE;
514
0
  }
515
0
  return fu_device_has_private_flag_quark(self, flag_quark);
516
0
}
517
518
/**
519
 * fu_device_get_request_cnt:
520
 * @self: a #FuDevice
521
 * @request_kind: the type of request
522
 *
523
 * Returns the number of requests of a specific kind. This function is only
524
 * useful to the daemon, which uses it to synthesize artificial events for
525
 * plugins not yet ported to [class@Fwupd.Request].
526
 *
527
 * Returns: integer, usually 0
528
 *
529
 * Since: 1.6.2
530
 **/
531
guint
532
fu_device_get_request_cnt(FuDevice *self, FwupdRequestKind request_kind)
533
0
{
534
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
535
0
  g_return_val_if_fail(FU_IS_DEVICE(self), G_MAXUINT);
536
0
  g_return_val_if_fail(request_kind < FWUPD_REQUEST_KIND_LAST, G_MAXUINT);
537
0
  return priv->request_cnts[request_kind];
538
0
}
539
540
/**
541
 * fu_device_get_possible_plugins:
542
 * @self: a #FuDevice
543
 *
544
 * Gets the list of possible plugin names, typically added from quirk files.
545
 *
546
 * Returns: (element-type utf8) (transfer container): plugin names
547
 *
548
 * Since: 1.3.3
549
 **/
550
GPtrArray *
551
fu_device_get_possible_plugins(FuDevice *self)
552
0
{
553
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
554
0
  return g_ptr_array_ref(priv->possible_plugins);
555
0
}
556
557
/**
558
 * fu_device_add_possible_plugin:
559
 * @self: a #FuDevice
560
 * @plugin: a plugin name, e.g. `dfu`
561
 *
562
 * Adds a plugin name to the list of plugins that *might* be able to handle this
563
 * device. This is typically called from a quirk handler.
564
 *
565
 * Duplicate plugin names are ignored.
566
 *
567
 * Since: 1.5.1
568
 **/
569
void
570
fu_device_add_possible_plugin(FuDevice *self, const gchar *plugin)
571
0
{
572
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
573
574
0
  g_return_if_fail(FU_IS_DEVICE(self));
575
0
  g_return_if_fail(plugin != NULL);
576
577
  /* add if it does not already exist */
578
0
  if (g_ptr_array_find_with_equal_func(priv->possible_plugins, plugin, g_str_equal, NULL))
579
0
    return;
580
0
  g_ptr_array_add(priv->possible_plugins, g_strdup(plugin));
581
0
}
582
583
/**
584
 * fu_device_retry_add_recovery:
585
 * @self: a #FuDevice
586
 * @domain: a #GQuark, or %0 for all domains
587
 * @code: a #GError code
588
 * @func: (scope async) (nullable): a function to recover the device
589
 *
590
 * Sets the optional function to be called when fu_device_retry() fails, which
591
 * is possibly a device reset.
592
 *
593
 * If @func is %NULL then recovery is not possible and an error is returned
594
 * straight away.
595
 *
596
 * Since: 1.4.0
597
 **/
598
void
599
fu_device_retry_add_recovery(FuDevice *self, GQuark domain, gint code, FuDeviceRetryFunc func)
600
0
{
601
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
602
0
  FuDeviceRetryRecovery *rec;
603
604
0
  g_return_if_fail(FU_IS_DEVICE(self));
605
0
  g_return_if_fail(domain != 0);
606
607
  /* ensure */
608
0
  if (priv->retry_recs == NULL)
609
0
    priv->retry_recs = g_ptr_array_new_with_free_func(g_free);
610
611
0
  rec = g_new(FuDeviceRetryRecovery, 1);
612
0
  rec->domain = domain;
613
0
  rec->code = code;
614
0
  rec->recovery_func = func;
615
0
  g_ptr_array_add(priv->retry_recs, rec);
616
0
}
617
618
/**
619
 * fu_device_retry_set_delay:
620
 * @self: a #FuDevice
621
 * @delay: delay in ms
622
 *
623
 * Sets the recovery delay between failed retries.
624
 *
625
 * Since: 1.4.0
626
 **/
627
void
628
fu_device_retry_set_delay(FuDevice *self, guint delay)
629
0
{
630
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
631
0
  g_return_if_fail(FU_IS_DEVICE(self));
632
0
  priv->retry_delay = delay;
633
0
}
634
635
/**
636
 * fu_device_retry_full:
637
 * @self: a #FuDevice
638
 * @func: (scope async) (closure user_data): a function to execute
639
 * @count: the number of tries to try the function
640
 * @delay: the delay between each try in ms
641
 * @user_data: (nullable): a helper to pass to @func
642
 * @error: (nullable): optional return location for an error
643
 *
644
 * Calls a specific function a number of times, optionally handling the error
645
 * with a reset action.
646
 *
647
 * If fu_device_retry_add_recovery() has not been used then all errors are
648
 * considered non-fatal until the last try.
649
 *
650
 * If the reset function returns %FALSE, then the function returns straight away
651
 * without processing any pending retries.
652
 *
653
 * Since: 1.5.5
654
 **/
655
gboolean
656
fu_device_retry_full(FuDevice *self,
657
         FuDeviceRetryFunc func,
658
         guint count,
659
         guint delay,
660
         gpointer user_data,
661
         GError **error)
662
0
{
663
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
664
665
0
  g_return_val_if_fail(FU_IS_DEVICE(self), FALSE);
666
0
  g_return_val_if_fail(func != NULL, FALSE);
667
0
  g_return_val_if_fail(count >= 1, FALSE);
668
0
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
669
670
0
  for (guint i = 0;; i++) {
671
0
    g_autoptr(GError) error_local = NULL;
672
673
    /* delay */
674
0
    if (i > 0)
675
0
      fu_device_sleep(self, delay);
676
677
    /* run function, if success return success */
678
0
    if (func(self, user_data, &error_local))
679
0
      break;
680
681
    /* sanity check */
682
0
    if (error_local == NULL) {
683
0
      g_set_error(error,
684
0
            FWUPD_ERROR,
685
0
            FWUPD_ERROR_INTERNAL,
686
0
            "exec failed but no error set!");
687
0
      return FALSE;
688
0
    }
689
690
    /* too many retries */
691
0
    if (i >= count - 1) {
692
0
      g_propagate_prefixed_error(error,
693
0
               g_steal_pointer(&error_local),
694
0
               "failed after %u retries: ",
695
0
               count);
696
0
      return FALSE;
697
0
    }
698
699
    /* show recoverable error on the console */
700
0
    if (priv->retry_recs == NULL || priv->retry_recs->len == 0) {
701
0
      g_info("failed on try %u of %u: %s", i + 1, count, error_local->message);
702
0
      continue;
703
0
    }
704
705
    /* find the condition that matches */
706
0
    for (guint j = 0; j < priv->retry_recs->len; j++) {
707
0
      FuDeviceRetryRecovery *rec = g_ptr_array_index(priv->retry_recs, j);
708
0
      if (g_error_matches(error_local, rec->domain, rec->code)) {
709
0
        if (rec->recovery_func != NULL) {
710
0
          if (!rec->recovery_func(self, user_data, error))
711
0
            return FALSE;
712
0
        } else {
713
0
          g_propagate_prefixed_error(
714
0
              error,
715
0
              g_steal_pointer(&error_local),
716
0
              "device recovery not possible: ");
717
0
          return FALSE;
718
0
        }
719
0
      }
720
0
    }
721
0
  }
722
723
  /* success */
724
0
  return TRUE;
725
0
}
726
727
/**
728
 * fu_device_retry:
729
 * @self: a #FuDevice
730
 * @func: (scope async) (closure user_data): a function to execute
731
 * @count: the number of tries to try the function
732
 * @user_data: (nullable): a helper to pass to @func
733
 * @error: (nullable): optional return location for an error
734
 *
735
 * Calls a specific function a number of times, optionally handling the error
736
 * with a reset action.
737
 *
738
 * If fu_device_retry_add_recovery() has not been used then all errors are
739
 * considered non-fatal until the last try.
740
 *
741
 * If the reset function returns %FALSE, then the function returns straight away
742
 * without processing any pending retries.
743
 *
744
 * Since: 1.4.0
745
 **/
746
gboolean
747
fu_device_retry(FuDevice *self,
748
    FuDeviceRetryFunc func,
749
    guint count,
750
    gpointer user_data,
751
    GError **error)
752
0
{
753
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
754
0
  return fu_device_retry_full(self, func, count, priv->retry_delay, user_data, error);
755
0
}
756
757
/**
758
 * fu_device_sleep:
759
 * @self: a #FuDevice
760
 * @delay_ms: delay in milliseconds
761
 *
762
 * Delays program execution up to 100 seconds, unless the device is emulated where no delays is
763
 * performed.
764
 *
765
 * Long unavoidable delays (more than 1 second) should really use `fu_device_sleep_full()` so that
766
 * the percentage progress bar is updated.
767
 *
768
 * Since: 1.8.11
769
 **/
770
void
771
fu_device_sleep(FuDevice *self, guint delay_ms)
772
0
{
773
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
774
775
0
  g_return_if_fail(FU_IS_DEVICE(self));
776
0
  g_return_if_fail(delay_ms < 100000);
777
778
0
  if (fu_device_has_flag(self, FWUPD_DEVICE_FLAG_EMULATED))
779
0
    return;
780
0
  if (priv->proxy != NULL && fu_device_has_flag(priv->proxy, FWUPD_DEVICE_FLAG_EMULATED))
781
0
    return;
782
0
  if (delay_ms > 0)
783
0
    g_usleep(delay_ms * 1000);
784
0
}
785
786
/**
787
 * fu_device_sleep_full:
788
 * @self: a #FuDevice
789
 * @delay_ms: delay in milliseconds
790
 * @progress: a #FuProgress
791
 *
792
 * Delays program execution up to 1000 seconds, unless the device is emulated where no delays is
793
 * performed.
794
 *
795
 * Since: 1.8.11
796
 **/
797
void
798
fu_device_sleep_full(FuDevice *self, guint delay_ms, FuProgress *progress)
799
0
{
800
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
801
802
0
  g_return_if_fail(FU_IS_DEVICE(self));
803
0
  g_return_if_fail(delay_ms < 1000000);
804
0
  g_return_if_fail(FU_IS_PROGRESS(progress));
805
806
0
  if (fu_device_has_flag(self, FWUPD_DEVICE_FLAG_EMULATED))
807
0
    return;
808
0
  if (priv->proxy != NULL && fu_device_has_flag(priv->proxy, FWUPD_DEVICE_FLAG_EMULATED))
809
0
    return;
810
0
  if (delay_ms > 0)
811
0
    fu_progress_sleep(progress, delay_ms);
812
0
}
813
814
/**
815
 * fu_device_set_contents:
816
 * @self: a #FuDevice
817
 * @filename: full path to a file
818
 * @stream: data to write
819
 * @progress: a #FuProgress
820
 * @error: (nullable): optional return location for an error
821
 *
822
 * Writes @stream to @filename, emulating if required.
823
 *
824
 * Returns: %TRUE on success
825
 *
826
 * Since: 2.0.2
827
 **/
828
gboolean
829
fu_device_set_contents(FuDevice *self,
830
           const gchar *filename,
831
           GInputStream *stream,
832
           FuProgress *progress,
833
           GError **error)
834
0
{
835
0
  FuDeviceEvent *event = NULL;
836
0
  g_autofree gchar *event_id = NULL;
837
0
  g_autoptr(FuChunkArray) chunks = NULL;
838
0
  g_autoptr(GByteArray) buf_tagged = g_byte_array_new();
839
0
  g_autoptr(GFile) file = NULL;
840
0
  g_autoptr(GOutputStream) ostr = NULL;
841
842
0
  g_return_val_if_fail(FU_IS_DEVICE(self), FALSE);
843
0
  g_return_val_if_fail(filename != NULL, FALSE);
844
0
  g_return_val_if_fail(G_IS_INPUT_STREAM(stream), FALSE);
845
0
  g_return_val_if_fail(FU_IS_PROGRESS(progress), FALSE);
846
0
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
847
848
  /* need event ID */
849
0
  if (fu_device_has_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_EMULATED) ||
850
0
      fu_context_has_flag(fu_device_get_context(FU_DEVICE(self)),
851
0
        FU_CONTEXT_FLAG_SAVE_EVENTS)) {
852
0
    event_id = g_strdup_printf("SetContents:Filename=%s", filename);
853
0
  }
854
855
  /* emulated */
856
0
  if (fu_device_has_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_EMULATED)) {
857
0
    g_autoptr(GBytes) blob1 = NULL;
858
0
    g_autoptr(GBytes) blob2 = NULL;
859
860
0
    event = fu_device_load_event(FU_DEVICE(self), event_id, error);
861
0
    if (event == NULL)
862
0
      return FALSE;
863
0
    blob1 = fu_device_event_get_bytes(event, "Data", error);
864
0
    if (blob1 == NULL)
865
0
      return FALSE;
866
0
    blob2 = fu_input_stream_read_bytes(stream, 0x0, G_MAXSIZE, progress, error);
867
0
    if (blob2 == NULL)
868
0
      return FALSE;
869
0
    return fu_bytes_compare(blob1, blob2, error);
870
0
  }
871
872
  /* save */
873
0
  if (event_id != NULL)
874
0
    event = fu_device_save_event(FU_DEVICE(self), event_id);
875
876
  /* open file */
877
0
  file = g_file_new_for_path(filename);
878
0
  ostr = G_OUTPUT_STREAM(g_file_replace(file, NULL, FALSE, G_FILE_CREATE_NONE, NULL, error));
879
0
  if (ostr == NULL)
880
0
    return FALSE;
881
882
  /* write in 32k chunks */
883
0
  chunks = fu_chunk_array_new_from_stream(stream,
884
0
            FU_CHUNK_ADDR_OFFSET_NONE,
885
0
            FU_CHUNK_PAGESZ_NONE,
886
0
            0x8000,
887
0
            error);
888
0
  if (chunks == NULL)
889
0
    return FALSE;
890
0
  fu_progress_set_id(progress, G_STRLOC);
891
0
  fu_progress_set_steps(progress, fu_chunk_array_length(chunks));
892
0
  for (guint i = 0; i < fu_chunk_array_length(chunks); i++) {
893
0
    g_autoptr(FuChunk) chk = NULL;
894
0
    g_autoptr(GBytes) blob = NULL;
895
896
0
    chk = fu_chunk_array_index(chunks, i, error);
897
0
    if (chk == NULL)
898
0
      return FALSE;
899
0
    blob = fu_chunk_get_bytes(chk);
900
0
    if (!fu_output_stream_write_bytes(ostr, blob, NULL, error))
901
0
      return FALSE;
902
903
    /* save */
904
0
    if (event != NULL)
905
0
      fu_byte_array_append_bytes(buf_tagged, blob);
906
907
    /* progress */
908
0
    fu_progress_step_done(progress);
909
0
  }
910
911
  /* save response */
912
0
  if (event != NULL)
913
0
    fu_device_event_set_data(event, "Data", buf_tagged->data, buf_tagged->len);
914
915
  /* success */
916
0
  return TRUE;
917
0
}
918
919
/**
920
 * fu_device_set_contents_bytes:
921
 * @self: a #FuDevice
922
 * @filename: full path to a file
923
 * @blob: data to write
924
 * @progress: (nullable): optional #FuProgress
925
 * @error: (nullable): optional return location for an error
926
 *
927
 * Writes @blob to @filename, emulating if required.
928
 *
929
 * Returns: %TRUE on success
930
 *
931
 * Since: 2.0.2
932
 **/
933
gboolean
934
fu_device_set_contents_bytes(FuDevice *self,
935
           const gchar *filename,
936
           GBytes *blob,
937
           FuProgress *progress,
938
           GError **error)
939
0
{
940
0
  g_autoptr(GInputStream) stream = NULL;
941
942
0
  g_return_val_if_fail(FU_IS_DEVICE(self), FALSE);
943
0
  g_return_val_if_fail(filename != NULL, FALSE);
944
0
  g_return_val_if_fail(blob != NULL, FALSE);
945
0
  g_return_val_if_fail(progress == NULL || FU_IS_PROGRESS(progress), FALSE);
946
0
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
947
948
0
  stream = g_memory_input_stream_new_from_bytes(blob);
949
0
  return fu_device_set_contents(self, filename, stream, progress, error);
950
0
}
951
952
/**
953
 * fu_device_get_contents_bytes:
954
 * @self: a #FuDevice
955
 * @filename: full path to a file
956
 * @count: maximum number of bytes to read
957
 * @progress: (nullable): optional #FuProgress
958
 * @error: (nullable): optional return location for an error
959
 *
960
 * Reads a blob of data from the file, emulating if required.
961
 *
962
 * Returns: (transfer full): a #GBytes, or %NULL on error
963
 *
964
 * Since: 2.0.2
965
 **/
966
GBytes *
967
fu_device_get_contents_bytes(FuDevice *self,
968
           const gchar *filename,
969
           gsize count,
970
           FuProgress *progress,
971
           GError **error)
972
0
{
973
0
  FuDeviceEvent *event = NULL;
974
0
  g_autofree gchar *event_id = NULL;
975
0
  g_autoptr(GBytes) blob = NULL;
976
0
  g_autoptr(GInputStream) istr = NULL;
977
978
0
  g_return_val_if_fail(FU_IS_DEVICE(self), NULL);
979
0
  g_return_val_if_fail(filename != NULL, NULL);
980
0
  g_return_val_if_fail(progress == NULL || FU_IS_PROGRESS(progress), NULL);
981
0
  g_return_val_if_fail(error == NULL || *error == NULL, NULL);
982
983
  /* need event ID */
984
0
  if (fu_device_has_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_EMULATED) ||
985
0
      fu_context_has_flag(fu_device_get_context(FU_DEVICE(self)),
986
0
        FU_CONTEXT_FLAG_SAVE_EVENTS)) {
987
0
    event_id = g_strdup_printf("GetContents:Filename=%s", filename);
988
0
  }
989
990
  /* emulated */
991
0
  if (fu_device_has_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_EMULATED)) {
992
0
    event = fu_device_load_event(FU_DEVICE(self), event_id, error);
993
0
    if (event == NULL)
994
0
      return NULL;
995
0
    return fu_device_event_get_bytes(event, "Data", error);
996
0
  }
997
998
  /* save */
999
0
  if (event_id != NULL)
1000
0
    event = fu_device_save_event(FU_DEVICE(self), event_id);
1001
1002
  /* open for reading */
1003
0
  istr = fu_input_stream_from_path(filename, error);
1004
0
  if (istr == NULL)
1005
0
    return NULL;
1006
0
  blob = fu_input_stream_read_bytes(istr, 0, count, progress, error);
1007
0
  if (blob == NULL)
1008
0
    return NULL;
1009
1010
  /* save response */
1011
0
  if (event != NULL)
1012
0
    fu_device_event_set_bytes(event, "Data", blob);
1013
1014
  /* success */
1015
0
  return g_steal_pointer(&blob);
1016
0
}
1017
1018
/**
1019
 * fu_device_get_contents:
1020
 * @self: a #FuDevice
1021
 * @filename: full path to a file
1022
 * @count: maximum number of bytes to read
1023
 * @progress: (nullable): optional #FuProgress
1024
 * @error: (nullable): optional return location for an error
1025
 *
1026
 * Reads a blob of ASCII text from the file, emulating if required.
1027
 *
1028
 * Returns: (transfer full): a #GBytes, or %NULL on error
1029
 *
1030
 * Since: 2.0.12
1031
 **/
1032
gchar *
1033
fu_device_get_contents(FuDevice *self,
1034
           const gchar *filename,
1035
           gsize count,
1036
           FuProgress *progress,
1037
           GError **error)
1038
0
{
1039
0
  FuDeviceEvent *event = NULL;
1040
0
  g_autofree gchar *event_id = NULL;
1041
0
  g_autofree gchar *str = NULL;
1042
0
  g_autoptr(GBytes) blob = NULL;
1043
0
  g_autoptr(GInputStream) istr = NULL;
1044
1045
0
  g_return_val_if_fail(FU_IS_DEVICE(self), NULL);
1046
0
  g_return_val_if_fail(filename != NULL, NULL);
1047
0
  g_return_val_if_fail(progress == NULL || FU_IS_PROGRESS(progress), NULL);
1048
0
  g_return_val_if_fail(error == NULL || *error == NULL, NULL);
1049
1050
  /* need event ID */
1051
0
  if (fu_device_has_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_EMULATED) ||
1052
0
      fu_context_has_flag(fu_device_get_context(FU_DEVICE(self)),
1053
0
        FU_CONTEXT_FLAG_SAVE_EVENTS)) {
1054
0
    event_id = g_strdup_printf("GetContents:Filename=%s", filename);
1055
0
  }
1056
1057
  /* emulated */
1058
0
  if (fu_device_has_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_EMULATED)) {
1059
0
    event = fu_device_load_event(FU_DEVICE(self), event_id, error);
1060
0
    if (event == NULL)
1061
0
      return NULL;
1062
0
    return g_strdup(fu_device_event_get_str(event, "Data", error));
1063
0
  }
1064
1065
  /* save */
1066
0
  if (event_id != NULL)
1067
0
    event = fu_device_save_event(FU_DEVICE(self), event_id);
1068
1069
  /* open for reading */
1070
0
  istr = fu_input_stream_from_path(filename, error);
1071
0
  if (istr == NULL)
1072
0
    return NULL;
1073
0
  blob = fu_input_stream_read_bytes(istr, 0, count, progress, error);
1074
0
  if (blob == NULL)
1075
0
    return NULL;
1076
0
  str = fu_strsafe_bytes(blob, G_MAXSIZE);
1077
0
  if (str == NULL) {
1078
0
    g_set_error_literal(error,
1079
0
            FWUPD_ERROR,
1080
0
            FWUPD_ERROR_INVALID_DATA,
1081
0
            "invalid ASCII data");
1082
0
    return NULL;
1083
0
  }
1084
1085
  /* save response */
1086
0
  if (event != NULL)
1087
0
    fu_device_event_set_str(event, "Data", str);
1088
1089
  /* success */
1090
0
  return g_steal_pointer(&str);
1091
0
}
1092
1093
/**
1094
 * fu_device_get_smbios_string:
1095
 * @self: a #FuDevice
1096
 * @type: a SMBIOS structure type, e.g. %FU_SMBIOS_STRUCTURE_TYPE_BIOS
1097
 * @length: expected length of the structure, or %FU_SMBIOS_STRUCTURE_LENGTH_ANY
1098
 * @offset: a SMBIOS offset
1099
 * @error: (nullable): optional return location for an error
1100
 *
1101
 * Gets a hardware SMBIOS string.
1102
 *
1103
 * The @type and @offset can be referenced from the DMTF SMBIOS specification:
1104
 * https://www.dmtf.org/sites/default/files/standards/documents/DSP0134_3.1.1.pdf
1105
 *
1106
 * Returns: a string, or %NULL
1107
 *
1108
 * Since: 2.0.10
1109
 **/
1110
const gchar *
1111
fu_device_get_smbios_string(FuDevice *self,
1112
          guint8 type,
1113
          guint8 length,
1114
          guint8 offset,
1115
          GError **error)
1116
0
{
1117
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
1118
0
  FuDeviceEvent *event = NULL;
1119
0
  const gchar *str;
1120
0
  g_autofree gchar *event_id = NULL;
1121
0
  g_autoptr(GError) error_local = NULL;
1122
1123
0
  g_return_val_if_fail(FU_IS_DEVICE(self), NULL);
1124
0
  g_return_val_if_fail(error == NULL || *error == NULL, NULL);
1125
1126
  /* need event ID */
1127
0
  if (fu_device_has_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_EMULATED) ||
1128
0
      fu_context_has_flag(fu_device_get_context(FU_DEVICE(self)),
1129
0
        FU_CONTEXT_FLAG_SAVE_EVENTS)) {
1130
0
    event_id = g_strdup_printf("GetSmbiosString:"
1131
0
             "Type=0x%02x,"
1132
0
             "Length=0x%02x,"
1133
0
             "Offset=0x%02x",
1134
0
             type,
1135
0
             length,
1136
0
             offset);
1137
0
  }
1138
1139
  /* emulated */
1140
0
  if (fu_device_has_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_EMULATED)) {
1141
0
    event = fu_device_load_event(FU_DEVICE(self), event_id, error);
1142
0
    if (event == NULL)
1143
0
      return NULL;
1144
0
    if (!fu_device_event_check_error(event, error))
1145
0
      return NULL;
1146
0
    return fu_device_event_get_str(event, "Data", error);
1147
0
  }
1148
1149
  /* save */
1150
0
  if (event_id != NULL)
1151
0
    event = fu_device_save_event(FU_DEVICE(self), event_id);
1152
1153
  /* use context */
1154
0
  str = fu_context_get_smbios_string(priv->ctx, type, length, offset, &error_local);
1155
0
  if (str == NULL) {
1156
0
    if (event != NULL)
1157
0
      fu_device_event_set_error(event, error_local);
1158
0
    g_propagate_error(error, g_steal_pointer(&error_local));
1159
0
    return NULL;
1160
0
  }
1161
1162
  /* save response */
1163
0
  if (event != NULL)
1164
0
    fu_device_event_set_str(event, "Data", str);
1165
1166
  /* success */
1167
0
  return str;
1168
0
}
1169
1170
/**
1171
 * fu_device_query_file_exists:
1172
 * @self: a #FuDevice
1173
 * @filename: filename
1174
 * @exists: (out): if @filename exists
1175
 * @error: (nullable): optional return location for an error
1176
 *
1177
 * Checks if a file exists.
1178
 *
1179
 * Returns: %TRUE for success
1180
 *
1181
 * Since: 2.0.2
1182
 **/
1183
gboolean
1184
fu_device_query_file_exists(FuDevice *self, const gchar *filename, gboolean *exists, GError **error)
1185
0
{
1186
0
  FuDeviceEvent *event = NULL;
1187
0
  gint64 value;
1188
0
  g_autofree gchar *event_id = NULL;
1189
1190
0
  g_return_val_if_fail(FU_IS_DEVICE(self), FALSE);
1191
0
  g_return_val_if_fail(filename != NULL, FALSE);
1192
0
  g_return_val_if_fail(exists != NULL, FALSE);
1193
0
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
1194
1195
  /* emulated */
1196
0
  if (fu_device_has_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_EMULATED) ||
1197
0
      fu_context_has_flag(fu_device_get_context(FU_DEVICE(self)),
1198
0
        FU_CONTEXT_FLAG_SAVE_EVENTS)) {
1199
0
    event_id = g_strdup_printf("FileExists:Filename=%s", filename);
1200
0
  }
1201
1202
  /* emulated */
1203
0
  if (fu_device_has_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_EMULATED)) {
1204
0
    event = fu_device_load_event(FU_DEVICE(self), event_id, error);
1205
0
    if (event == NULL)
1206
0
      return FALSE;
1207
0
    value = fu_device_event_get_i64(event, "Exists", error);
1208
0
    if (value == G_MAXINT64)
1209
0
      return FALSE;
1210
0
    *exists = value == 1;
1211
0
    return TRUE;
1212
0
  }
1213
1214
  /* save */
1215
0
  if (event_id != NULL)
1216
0
    event = fu_device_save_event(FU_DEVICE(self), event_id);
1217
1218
  /* check, and save result */
1219
0
  *exists = g_file_test(filename, G_FILE_TEST_EXISTS);
1220
0
  if (event != NULL)
1221
0
    fu_device_event_set_i64(event, "Exists", *exists ? 1 : 0);
1222
1223
  /* success */
1224
0
  return TRUE;
1225
0
}
1226
1227
static gboolean
1228
fu_device_poll_locker_open_cb(GObject *device, GError **error)
1229
0
{
1230
0
  FuDevice *self = FU_DEVICE(device);
1231
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
1232
0
  g_atomic_int_inc(&priv->poll_locker_cnt);
1233
0
  return TRUE;
1234
0
}
1235
1236
static gboolean
1237
fu_device_poll_locker_close_cb(GObject *device, GError **error)
1238
0
{
1239
0
  FuDevice *self = FU_DEVICE(device);
1240
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
1241
0
  g_atomic_int_dec_and_test(&priv->poll_locker_cnt);
1242
0
  return TRUE;
1243
0
}
1244
1245
/**
1246
 * fu_device_poll_locker_new:
1247
 * @self: a #FuDevice
1248
 * @error: (nullable): optional return location for an error
1249
 *
1250
 * Returns a device locker that prevents polling on the device. If there are no open poll lockers
1251
 * then the poll callback will be called.
1252
 *
1253
 * Use %FU_DEVICE_PRIVATE_FLAG_AUTO_PAUSE_POLLING to opt into this functionality.
1254
 *
1255
 * Returns: (transfer full): a #FuDeviceLocker
1256
 *
1257
 * Since: 1.8.1
1258
 **/
1259
FuDeviceLocker *
1260
fu_device_poll_locker_new(FuDevice *self, GError **error)
1261
0
{
1262
0
  g_return_val_if_fail(FU_IS_DEVICE(self), NULL);
1263
0
  g_return_val_if_fail(error == NULL || *error == NULL, NULL);
1264
1265
0
  return fu_device_locker_new_full(self,
1266
0
           fu_device_poll_locker_open_cb,
1267
0
           fu_device_poll_locker_close_cb,
1268
0
           error);
1269
0
}
1270
1271
/**
1272
 * fu_device_poll:
1273
 * @self: a #FuDevice
1274
 * @error: (nullable): optional return location for an error
1275
 *
1276
 * Polls a device, typically querying the hardware for status.
1277
 *
1278
 * Returns: %TRUE for success
1279
 *
1280
 * Since: 1.1.2
1281
 **/
1282
gboolean
1283
fu_device_poll(FuDevice *self, GError **error)
1284
0
{
1285
0
  FuDeviceClass *device_class = FU_DEVICE_GET_CLASS(self);
1286
1287
0
  g_return_val_if_fail(FU_IS_DEVICE(self), FALSE);
1288
0
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
1289
1290
  /* subclassed */
1291
0
  if (device_class->poll != NULL) {
1292
0
    if (!device_class->poll(self, error))
1293
0
      return FALSE;
1294
0
  }
1295
0
  return TRUE;
1296
0
}
1297
1298
static gboolean
1299
fu_device_poll_cb(gpointer user_data)
1300
0
{
1301
0
  FuDevice *self = FU_DEVICE(user_data);
1302
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
1303
0
  g_autoptr(GError) error_local = NULL;
1304
1305
  /* device is being detached, written, read, or attached */
1306
0
  if (fu_device_has_private_flag(self, FU_DEVICE_PRIVATE_FLAG_AUTO_PAUSE_POLLING) &&
1307
0
      priv->poll_locker_cnt > 0) {
1308
0
    g_debug("ignoring poll callback as an action is in progress");
1309
0
    return G_SOURCE_CONTINUE;
1310
0
  }
1311
1312
0
  if (!fu_device_poll(self, &error_local)) {
1313
0
    g_warning("disabling polling: %s", error_local->message);
1314
0
    priv->poll_id = 0;
1315
0
    return G_SOURCE_REMOVE;
1316
0
  }
1317
0
  return G_SOURCE_CONTINUE;
1318
0
}
1319
1320
/**
1321
 * fu_device_set_poll_interval:
1322
 * @self: a #FuPlugin
1323
 * @interval: duration in ms, or 0 to disable
1324
 *
1325
 * Polls the hardware every interval period. If the subclassed `->poll()` method
1326
 * returns %FALSE then a warning is printed to the console and the poll is
1327
 * disabled until the next call to fu_device_set_poll_interval().
1328
 *
1329
 * Since: 1.1.2
1330
 **/
1331
void
1332
fu_device_set_poll_interval(FuDevice *self, guint interval)
1333
0
{
1334
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
1335
1336
0
  g_return_if_fail(FU_IS_DEVICE(self));
1337
1338
0
  if (priv->poll_id != 0) {
1339
0
    g_source_remove(priv->poll_id);
1340
0
    priv->poll_id = 0;
1341
0
  }
1342
0
  if (interval == 0)
1343
0
    return;
1344
0
  if (interval % 1000 == 0) {
1345
0
    priv->poll_id = g_timeout_add_seconds(interval / 1000, fu_device_poll_cb, self);
1346
0
  } else {
1347
0
    priv->poll_id = g_timeout_add(interval, fu_device_poll_cb, self);
1348
0
  }
1349
0
}
1350
1351
/**
1352
 * fu_device_get_order:
1353
 * @self: a #FuPlugin
1354
 *
1355
 * Gets the device order, where higher numbers are installed after lower
1356
 * numbers.
1357
 *
1358
 * Returns: the integer value
1359
 *
1360
 * Since: 1.0.8
1361
 **/
1362
gint
1363
fu_device_get_order(FuDevice *self)
1364
0
{
1365
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
1366
0
  g_return_val_if_fail(FU_IS_DEVICE(self), 0);
1367
0
  return priv->order;
1368
0
}
1369
1370
/**
1371
 * fu_device_set_order:
1372
 * @self: a #FuDevice
1373
 * @order: an integer value
1374
 *
1375
 * Sets the device order, where higher numbers are installed after lower
1376
 * numbers.
1377
 *
1378
 * Since: 1.0.8
1379
 **/
1380
void
1381
fu_device_set_order(FuDevice *self, gint order)
1382
0
{
1383
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
1384
0
  g_return_if_fail(FU_IS_DEVICE(self));
1385
0
  priv->order = order;
1386
0
}
1387
1388
/**
1389
 * fu_device_get_priority:
1390
 * @self: a #FuPlugin
1391
 *
1392
 * Gets the device priority, where higher numbers are better.
1393
 *
1394
 * Returns: the integer value
1395
 *
1396
 * Since: 1.1.1
1397
 **/
1398
guint
1399
fu_device_get_priority(FuDevice *self)
1400
0
{
1401
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
1402
0
  g_return_val_if_fail(FU_IS_DEVICE(self), 0);
1403
0
  return priv->priority;
1404
0
}
1405
1406
/**
1407
 * fu_device_set_priority:
1408
 * @self: a #FuDevice
1409
 * @priority: an integer value
1410
 *
1411
 * Sets the device priority, where higher numbers are better.
1412
 *
1413
 * Since: 1.1.1
1414
 **/
1415
void
1416
fu_device_set_priority(FuDevice *self, guint priority)
1417
0
{
1418
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
1419
0
  g_return_if_fail(FU_IS_DEVICE(self));
1420
0
  priv->priority = priority;
1421
0
}
1422
1423
/**
1424
 * fu_device_get_equivalent_id:
1425
 * @self: a #FuDevice
1426
 *
1427
 * Gets any equivalent ID for a device
1428
 *
1429
 * Returns: (transfer none): a #gchar or NULL
1430
 *
1431
 * Since: 0.6.1
1432
 **/
1433
const gchar *
1434
fu_device_get_equivalent_id(FuDevice *self)
1435
0
{
1436
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
1437
0
  g_return_val_if_fail(FU_IS_DEVICE(self), NULL);
1438
0
  return priv->equivalent_id;
1439
0
}
1440
1441
/**
1442
 * fu_device_set_equivalent_id:
1443
 * @self: a #FuDevice
1444
 * @equivalent_id: (nullable): a string
1445
 *
1446
 * Sets any equivalent ID for a device
1447
 *
1448
 * Since: 0.6.1
1449
 **/
1450
void
1451
fu_device_set_equivalent_id(FuDevice *self, const gchar *equivalent_id)
1452
0
{
1453
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
1454
0
  g_return_if_fail(FU_IS_DEVICE(self));
1455
1456
  /* not changed */
1457
0
  if (g_strcmp0(priv->equivalent_id, equivalent_id) == 0)
1458
0
    return;
1459
1460
  /* sanity check */
1461
0
  if (!fwupd_device_id_is_valid(equivalent_id)) {
1462
0
    g_critical("%s is not a valid device ID", equivalent_id);
1463
0
    return;
1464
0
  }
1465
0
  if (g_strcmp0(equivalent_id, fu_device_get_id(self)) == 0) {
1466
0
    g_critical("%s is the same as this device ID", equivalent_id);
1467
0
    return;
1468
0
  }
1469
1470
0
  g_free(priv->equivalent_id);
1471
0
  priv->equivalent_id = g_strdup(equivalent_id);
1472
0
  g_object_notify(G_OBJECT(self), "equivalent-id");
1473
0
}
1474
1475
/**
1476
 * fu_device_get_parent:
1477
 * @self: a #FuDevice
1478
 *
1479
 * Gets any parent device. An parent device is logically "above" the current
1480
 * device and this may be reflected in client tools.
1481
 *
1482
 * This information also allows the plugin to optionally verify the parent
1483
 * device, for instance checking the parent device firmware version.
1484
 *
1485
 * The parent object is not refcounted and if destroyed this function will then
1486
 * return %NULL.
1487
 *
1488
 * Returns: (transfer none): a device or %NULL
1489
 *
1490
 * Since: 1.0.8
1491
 **/
1492
FuDevice *
1493
fu_device_get_parent(FuDevice *self)
1494
0
{
1495
0
  g_return_val_if_fail(FU_IS_DEVICE(self), NULL);
1496
0
  return FU_DEVICE(fwupd_device_get_parent(FWUPD_DEVICE(self)));
1497
0
}
1498
1499
/**
1500
 * fu_device_get_root:
1501
 * @self: a #FuDevice
1502
 *
1503
 * Gets the root parent device. A parent device is logically "above" the current
1504
 * device and this may be reflected in client tools.
1505
 *
1506
 * If there is no parent device defined, then @self is returned.
1507
 *
1508
 * Returns: (transfer full): a device
1509
 *
1510
 * Since: 1.4.0
1511
 **/
1512
FuDevice *
1513
fu_device_get_root(FuDevice *self)
1514
0
{
1515
0
  FuDevice *parent;
1516
0
  g_return_val_if_fail(FU_IS_DEVICE(self), NULL);
1517
0
  do {
1518
0
    parent = fu_device_get_parent(self);
1519
0
    if (parent != NULL)
1520
0
      self = parent;
1521
0
  } while (parent != NULL);
1522
0
  return g_object_ref(self);
1523
0
}
1524
1525
static void
1526
fu_device_set_composite_id(FuDevice *self, const gchar *composite_id)
1527
0
{
1528
0
  GPtrArray *children;
1529
1530
  /* subclassed simple setter */
1531
0
  fwupd_device_set_composite_id(FWUPD_DEVICE(self), composite_id);
1532
1533
  /* all children */
1534
0
  children = fu_device_get_children(self);
1535
0
  for (guint i = 0; i < children->len; i++) {
1536
0
    FuDevice *child_tmp = g_ptr_array_index(children, i);
1537
0
    fu_device_set_composite_id(child_tmp, composite_id);
1538
0
  }
1539
0
}
1540
1541
/**
1542
 * fu_device_set_parent:
1543
 * @self: a #FuDevice
1544
 * @parent: (nullable): a device
1545
 *
1546
 * Sets any parent device. An parent device is logically "above" the current
1547
 * device and this may be reflected in client tools.
1548
 *
1549
 * This information also allows the plugin to optionally verify the parent
1550
 * device, for instance checking the parent device firmware version.
1551
 *
1552
 * Since: 1.0.8
1553
 **/
1554
void
1555
fu_device_set_parent(FuDevice *self, FuDevice *parent)
1556
0
{
1557
0
  g_return_if_fail(FU_IS_DEVICE(self));
1558
1559
  /* debug */
1560
0
  if (parent != NULL) {
1561
0
    g_info("setting parent of %s [%s] to be %s [%s]",
1562
0
           fu_device_get_name(self),
1563
0
           fu_device_get_id(self),
1564
0
           fu_device_get_name(parent),
1565
0
           fu_device_get_id(parent));
1566
0
  }
1567
1568
  /* set the composite ID on the children and grandchildren */
1569
0
  if (parent != NULL)
1570
0
    fu_device_set_composite_id(self, fu_device_get_composite_id(parent));
1571
1572
  /* if the parent has a context, make the child inherit it */
1573
0
  if (parent != NULL) {
1574
0
    if (fu_device_get_context(self) == NULL && fu_device_get_context(parent) != NULL)
1575
0
      fu_device_set_context(self, fu_device_get_context(parent));
1576
0
  }
1577
1578
0
  fwupd_device_set_parent(FWUPD_DEVICE(self), FWUPD_DEVICE(parent));
1579
0
  g_object_notify(G_OBJECT(self), "parent");
1580
0
}
1581
1582
/* if the proxy sets this flag copy it to the logical device */
1583
static void
1584
fu_device_incorporate_from_proxy_flags(FuDevice *self, FuDevice *proxy)
1585
0
{
1586
0
  const FwupdDeviceFlags flags[] = {FWUPD_DEVICE_FLAG_EMULATED,
1587
0
            FWUPD_DEVICE_FLAG_UNREACHABLE};
1588
0
  for (guint i = 0; i < G_N_ELEMENTS(flags); i++) {
1589
0
    if (fu_device_has_flag(proxy, flags[i])) {
1590
0
      g_debug("propagating %s from proxy", fwupd_device_flag_to_string(flags[i]));
1591
0
      fu_device_add_flag(self, flags[i]);
1592
0
    }
1593
0
  }
1594
0
}
1595
1596
static void
1597
fu_device_proxy_flags_notify_cb(FuDevice *proxy, GParamSpec *pspec, gpointer user_data)
1598
0
{
1599
0
  FuDevice *self = FU_DEVICE(user_data);
1600
0
  fu_device_incorporate_from_proxy_flags(self, proxy);
1601
0
}
1602
1603
/**
1604
 * fu_device_set_proxy:
1605
 * @self: a #FuDevice
1606
 * @proxy: a device
1607
 *
1608
 * Sets any proxy device. A proxy device can be used to perform an action on
1609
 * behalf of another device, for instance attach()ing it after a successful
1610
 * update.
1611
 *
1612
 * If the %FU_DEVICE_PRIVATE_FLAG_REFCOUNTED_PROXY flag is present on the device then a *strong*
1613
 * reference is used, otherwise the proxy will be cleared if @proxy is destroyed.
1614
 *
1615
 * Since: 1.4.1
1616
 **/
1617
void
1618
fu_device_set_proxy(FuDevice *self, FuDevice *proxy)
1619
0
{
1620
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
1621
1622
0
  g_return_if_fail(FU_IS_DEVICE(self));
1623
1624
  /* unchanged */
1625
0
  if (proxy == priv->proxy)
1626
0
    return;
1627
1628
  /* disconnect from old proxy */
1629
0
  if (priv->proxy != NULL && priv->notify_flags_proxy_id != 0) {
1630
0
    g_signal_handler_disconnect(priv->proxy, priv->notify_flags_proxy_id);
1631
0
    priv->notify_flags_proxy_id = 0;
1632
0
  }
1633
1634
  /* copy from proxy */
1635
0
  if (proxy != NULL) {
1636
0
    fu_device_incorporate(self, proxy, FU_DEVICE_INCORPORATE_FLAG_PHYSICAL_ID);
1637
0
    priv->notify_flags_proxy_id =
1638
0
        g_signal_connect(FWUPD_DEVICE(proxy),
1639
0
             "notify::flags",
1640
0
             G_CALLBACK(fu_device_proxy_flags_notify_cb),
1641
0
             self);
1642
0
    fu_device_incorporate_from_proxy_flags(self, proxy);
1643
0
  }
1644
1645
  /* sometimes strong, sometimes weak */
1646
0
  if (fu_device_has_private_flag_quark(self, quarks[QUARK_REFCOUNTED_PROXY])) {
1647
0
    g_set_object(&priv->proxy, proxy);
1648
0
    fu_device_set_target(self, proxy);
1649
0
  } else {
1650
0
    if (priv->proxy != NULL)
1651
0
      g_object_remove_weak_pointer(G_OBJECT(priv->proxy),
1652
0
                 (gpointer *)&priv->proxy);
1653
0
    if (proxy != NULL)
1654
0
      g_object_add_weak_pointer(G_OBJECT(proxy), (gpointer *)&priv->proxy);
1655
0
    priv->proxy = proxy;
1656
0
  }
1657
0
  g_object_notify(G_OBJECT(self), "proxy");
1658
0
}
1659
1660
/**
1661
 * fu_device_get_proxy:
1662
 * @self: a #FuDevice
1663
 *
1664
 * Gets any proxy device. A proxy device can be used to perform an action on
1665
 * behalf of another device, for instance attach()ing it after a successful
1666
 * update.
1667
 *
1668
 * Unless %FU_DEVICE_PRIVATE_FLAG_REFCOUNTED_PROXY is set, the proxy object is not refcounted and
1669
 * if destroyed this function will then return %NULL.
1670
 *
1671
 * Returns: (transfer none): a device or %NULL
1672
 *
1673
 * Since: 1.4.1
1674
 **/
1675
FuDevice *
1676
fu_device_get_proxy(FuDevice *self)
1677
0
{
1678
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
1679
0
  g_return_val_if_fail(FU_IS_DEVICE(self), NULL);
1680
0
  return priv->proxy;
1681
0
}
1682
1683
/**
1684
 * fu_device_get_proxy_with_fallback:
1685
 * @self: a #FuDevice
1686
 *
1687
 * Gets the proxy device if %FU_DEVICE_PRIVATE_FLAG_USE_PROXY_FALLBACK is set, falling back to the
1688
 * device itself.
1689
 *
1690
 * Returns: (transfer none): a device
1691
 *
1692
 * Since: 1.6.2
1693
 **/
1694
FuDevice *
1695
fu_device_get_proxy_with_fallback(FuDevice *self)
1696
0
{
1697
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
1698
0
  g_return_val_if_fail(FU_IS_DEVICE(self), NULL);
1699
0
  if (fu_device_has_private_flag(self, FU_DEVICE_PRIVATE_FLAG_USE_PROXY_FALLBACK) &&
1700
0
      priv->proxy != NULL)
1701
0
    return priv->proxy;
1702
0
  return self;
1703
0
}
1704
1705
/**
1706
 * fu_device_get_children:
1707
 * @self: a #FuDevice
1708
 *
1709
 * Gets any child devices. A child device is logically "below" the current
1710
 * device and this may be reflected in client tools.
1711
 *
1712
 * Returns: (transfer none) (element-type FuDevice): child devices
1713
 *
1714
 * Since: 1.0.8
1715
 **/
1716
GPtrArray *
1717
fu_device_get_children(FuDevice *self)
1718
0
{
1719
0
  g_return_val_if_fail(FU_IS_DEVICE(self), NULL);
1720
0
  return fwupd_device_get_children(FWUPD_DEVICE(self));
1721
0
}
1722
1723
/**
1724
 * fu_device_add_child:
1725
 * @self: a #FuDevice
1726
 * @child: Another #FuDevice
1727
 *
1728
 * Sets any child device. An child device is logically linked to the primary
1729
 * device in some way.
1730
 *
1731
 * Since: 1.0.8
1732
 **/
1733
void
1734
fu_device_add_child(FuDevice *self, FuDevice *child)
1735
0
{
1736
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
1737
0
  GPtrArray *children;
1738
0
  g_autoptr(GError) error = NULL;
1739
1740
0
  g_return_if_fail(FU_IS_DEVICE(self));
1741
0
  g_return_if_fail(FU_IS_DEVICE(child));
1742
1743
  /* if parent is emulated, child must be too */
1744
0
  if (fu_device_has_flag(self, FWUPD_DEVICE_FLAG_EMULATED))
1745
0
    fu_device_add_flag(child, FWUPD_DEVICE_FLAG_EMULATED);
1746
1747
  /* make tests easier */
1748
0
  fu_device_convert_instance_ids(child);
1749
1750
  /* add if the child does not already exist */
1751
0
  fwupd_device_add_child(FWUPD_DEVICE(self), FWUPD_DEVICE(child));
1752
1753
  /* propagate inhibits to children */
1754
0
  if (priv->inhibits != NULL &&
1755
0
      fu_device_has_private_flag(self, FU_DEVICE_PRIVATE_FLAG_INHIBIT_CHILDREN)) {
1756
0
    g_autoptr(GList) values = g_hash_table_get_values(priv->inhibits);
1757
0
    for (GList *l = values; l != NULL; l = l->next) {
1758
0
      FuDeviceInhibit *inhibit = (FuDeviceInhibit *)l->data;
1759
0
      fu_device_inhibit_full(child,
1760
0
                 inhibit->problem,
1761
0
                 inhibit->inhibit_id,
1762
0
                 inhibit->reason);
1763
0
    }
1764
0
  }
1765
1766
  /* ensure the parent has the MAX() of the children's removal delay  */
1767
0
  children = fu_device_get_children(self);
1768
0
  for (guint i = 0; i < children->len; i++) {
1769
0
    FuDevice *child_tmp = g_ptr_array_index(children, i);
1770
0
    guint remove_delay = fu_device_get_remove_delay(child_tmp);
1771
0
    if (remove_delay > priv->remove_delay) {
1772
0
      g_debug("setting remove delay to %ums as child is greater than %ums",
1773
0
        remove_delay,
1774
0
        priv->remove_delay);
1775
0
      priv->remove_delay = remove_delay;
1776
0
    }
1777
0
  }
1778
1779
  /* ensure the parent has the MAX() of the children's acquiesce delay  */
1780
0
  children = fu_device_get_children(self);
1781
0
  for (guint i = 0; i < children->len; i++) {
1782
0
    FuDevice *child_tmp = g_ptr_array_index(children, i);
1783
0
    guint acquiesce_delay = fu_device_get_acquiesce_delay(child_tmp);
1784
0
    if (acquiesce_delay > priv->acquiesce_delay) {
1785
0
      g_debug("setting acquiesce delay to %ums as child is greater than %ums",
1786
0
        acquiesce_delay,
1787
0
        priv->acquiesce_delay);
1788
0
      priv->acquiesce_delay = acquiesce_delay;
1789
0
    }
1790
0
  }
1791
1792
  /* ensure child has the parent acquiesce delay */
1793
0
  for (guint i = 0; i < children->len; i++) {
1794
0
    FuDevice *child_tmp = g_ptr_array_index(children, i);
1795
0
    fu_device_set_acquiesce_delay(child_tmp, priv->acquiesce_delay);
1796
0
  }
1797
1798
  /* copy from main device if unset */
1799
0
  fu_device_incorporate(
1800
0
      child,
1801
0
      self,
1802
0
      FU_DEVICE_INCORPORATE_FLAG_PHYSICAL_ID | FU_DEVICE_INCORPORATE_FLAG_BACKEND_ID |
1803
0
    FU_DEVICE_INCORPORATE_FLAG_REMOVE_DELAY |
1804
0
    FU_DEVICE_INCORPORATE_FLAG_ACQUIESCE_DELAY | FU_DEVICE_INCORPORATE_FLAG_VENDOR_IDS |
1805
0
    FU_DEVICE_INCORPORATE_FLAG_ICONS | FU_DEVICE_INCORPORATE_FLAG_VENDOR);
1806
1807
  /* ensure the ID is converted */
1808
0
  if (!fu_device_ensure_id(child, &error))
1809
0
    g_warning("failed to ensure child: %s", error->message);
1810
1811
  /* ensure the parent is also set on the child */
1812
0
  fu_device_set_parent(child, self);
1813
1814
  /* signal to the plugin in case this is done after setup */
1815
0
  g_signal_emit(self, signals[SIGNAL_CHILD_ADDED], 0, child);
1816
0
}
1817
1818
/**
1819
 * fu_device_remove_child:
1820
 * @self: a #FuDevice
1821
 * @child: Another #FuDevice
1822
 *
1823
 * Removes child device.
1824
 *
1825
 * Since: 1.6.2
1826
 **/
1827
void
1828
fu_device_remove_child(FuDevice *self, FuDevice *child)
1829
0
{
1830
0
  g_return_if_fail(FU_IS_DEVICE(self));
1831
0
  g_return_if_fail(FU_IS_DEVICE(child));
1832
1833
  /* proxy */
1834
0
  fwupd_device_remove_child(FWUPD_DEVICE(self), FWUPD_DEVICE(child));
1835
1836
  /* signal to the plugin */
1837
0
  g_signal_emit(self, signals[SIGNAL_CHILD_REMOVED], 0, child);
1838
0
}
1839
1840
/**
1841
 * fu_device_remove_children:
1842
 * @self: a #FuDevice
1843
 *
1844
 * Removes all child devices.
1845
 *
1846
 * Since: 2.0.0
1847
 **/
1848
void
1849
fu_device_remove_children(FuDevice *self)
1850
0
{
1851
0
  GPtrArray *children;
1852
1853
0
  g_return_if_fail(FU_IS_DEVICE(self));
1854
1855
  /* proxy */
1856
0
  fwupd_device_remove_children(FWUPD_DEVICE(self));
1857
1858
  /* signal to the plugin */
1859
0
  children = fu_device_get_children(self);
1860
0
  for (guint i = 0; i < children->len; i++) {
1861
0
    FuDevice *child = g_ptr_array_index(children, i);
1862
0
    g_signal_emit(self, signals[SIGNAL_CHILD_REMOVED], 0, child);
1863
0
  }
1864
0
}
1865
1866
static void
1867
fu_device_ensure_parent_guids(FuDevice *self)
1868
0
{
1869
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
1870
0
  if (priv->parent_guids != NULL)
1871
0
    return;
1872
0
  priv->parent_guids = g_ptr_array_new_with_free_func(g_free);
1873
0
}
1874
1875
/**
1876
 * fu_device_get_parent_guids:
1877
 * @self: a #FuDevice
1878
 *
1879
 * Gets any parent device GUIDs. If a device is added to the daemon that matches
1880
 * any GUIDs added from fu_device_add_parent_guid() then this device is marked the parent of @self.
1881
 *
1882
 * Returns: (transfer none) (element-type utf8): a list of GUIDs
1883
 *
1884
 * Since: 1.0.8
1885
 **/
1886
GPtrArray *
1887
fu_device_get_parent_guids(FuDevice *self)
1888
0
{
1889
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
1890
0
  g_return_val_if_fail(FU_IS_DEVICE(self), NULL);
1891
0
  fu_device_ensure_parent_guids(self);
1892
0
  return priv->parent_guids;
1893
0
}
1894
1895
/**
1896
 * fu_device_has_parent_guid:
1897
 * @self: a #FuDevice
1898
 * @guid: a GUID
1899
 *
1900
 * Searches the list of parent GUIDs for a string match.
1901
 *
1902
 * Returns: %TRUE if the parent GUID exists
1903
 *
1904
 * Since: 1.0.8
1905
 **/
1906
gboolean
1907
fu_device_has_parent_guid(FuDevice *self, const gchar *guid)
1908
0
{
1909
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
1910
1911
0
  g_return_val_if_fail(FU_IS_DEVICE(self), FALSE);
1912
0
  g_return_val_if_fail(guid != NULL, FALSE);
1913
1914
0
  if (priv->parent_guids == NULL)
1915
0
    return FALSE;
1916
0
  for (guint i = 0; i < priv->parent_guids->len; i++) {
1917
0
    const gchar *guid_tmp = g_ptr_array_index(priv->parent_guids, i);
1918
0
    if (g_strcmp0(guid_tmp, guid) == 0)
1919
0
      return TRUE;
1920
0
  }
1921
0
  return FALSE;
1922
0
}
1923
1924
/**
1925
 * fu_device_add_parent_guid:
1926
 * @self: a #FuDevice
1927
 * @guid: a GUID
1928
 *
1929
 * Sets any parent device using a GUID. An parent device is logically linked to
1930
 * the primary device in some way and can be added before or after @self.
1931
 *
1932
 * The GUIDs are searched in order, and so the order of adding GUIDs may be
1933
 * important if more than one parent device might match.
1934
 *
1935
 * If the parent device is removed, any children logically linked to it will
1936
 * also be removed.
1937
 *
1938
 * Since: 1.0.8
1939
 **/
1940
void
1941
fu_device_add_parent_guid(FuDevice *self, const gchar *guid)
1942
0
{
1943
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
1944
1945
0
  g_return_if_fail(FU_IS_DEVICE(self));
1946
0
  g_return_if_fail(guid != NULL);
1947
1948
  /* ensure */
1949
0
  fu_device_ensure_parent_guids(self);
1950
1951
  /* make valid */
1952
0
  if (!fwupd_guid_is_valid(guid)) {
1953
0
    g_autofree gchar *tmp = fwupd_guid_hash_string(guid);
1954
0
    if (fu_device_has_parent_guid(self, tmp))
1955
0
      return;
1956
0
    g_debug("using %s for %s", tmp, guid);
1957
0
    g_ptr_array_add(priv->parent_guids, g_steal_pointer(&tmp));
1958
0
    return;
1959
0
  }
1960
1961
  /* already valid */
1962
0
  if (fu_device_has_parent_guid(self, guid))
1963
0
    return;
1964
0
  g_ptr_array_add(priv->parent_guids, g_strdup(guid));
1965
0
}
1966
1967
/**
1968
 * fu_device_get_parent_physical_ids:
1969
 * @self: a #FuDevice
1970
 *
1971
 * Gets any parent device IDs. If a device is added to the daemon that matches
1972
 * the physical ID added from fu_device_add_parent_physical_id() then this
1973
 * device is marked the parent of @self.
1974
 *
1975
 * Returns: (transfer none) (element-type utf8) (nullable): a list of IDs
1976
 *
1977
 * Since: 1.6.2
1978
 **/
1979
GPtrArray *
1980
fu_device_get_parent_physical_ids(FuDevice *self)
1981
0
{
1982
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
1983
0
  g_return_val_if_fail(FU_IS_DEVICE(self), NULL);
1984
0
  return priv->parent_physical_ids;
1985
0
}
1986
1987
/**
1988
 * fu_device_has_parent_physical_id:
1989
 * @self: a #FuDevice
1990
 * @physical_id: a device physical ID
1991
 *
1992
 * Searches the list of parent IDs for a string match.
1993
 *
1994
 * Returns: %TRUE if the parent ID exists
1995
 *
1996
 * Since: 1.6.2
1997
 **/
1998
gboolean
1999
fu_device_has_parent_physical_id(FuDevice *self, const gchar *physical_id)
2000
0
{
2001
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
2002
2003
0
  g_return_val_if_fail(FU_IS_DEVICE(self), FALSE);
2004
0
  g_return_val_if_fail(physical_id != NULL, FALSE);
2005
2006
0
  if (priv->parent_physical_ids == NULL)
2007
0
    return FALSE;
2008
0
  for (guint i = 0; i < priv->parent_physical_ids->len; i++) {
2009
0
    const gchar *tmp = g_ptr_array_index(priv->parent_physical_ids, i);
2010
0
    if (g_strcmp0(tmp, physical_id) == 0)
2011
0
      return TRUE;
2012
0
  }
2013
0
  return FALSE;
2014
0
}
2015
2016
/**
2017
 * fu_device_add_parent_physical_id:
2018
 * @self: a #FuDevice
2019
 * @physical_id: a device physical ID
2020
 *
2021
 * Sets any parent device using the physical ID. An parent device is logically
2022
 * linked to the primary device in some way and can be added before or after @self.
2023
 *
2024
 * The IDs are searched in order, and so the order of adding IDs may be
2025
 * important if more than one parent device might match.
2026
 *
2027
 * Since: 1.6.2
2028
 **/
2029
void
2030
fu_device_add_parent_physical_id(FuDevice *self, const gchar *physical_id)
2031
0
{
2032
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
2033
2034
0
  g_return_if_fail(FU_IS_DEVICE(self));
2035
0
  g_return_if_fail(physical_id != NULL);
2036
2037
  /* ensure exists */
2038
0
  if (priv->parent_physical_ids == NULL)
2039
0
    priv->parent_physical_ids = g_ptr_array_new_with_free_func(g_free);
2040
2041
  /* already present */
2042
0
  if (fu_device_has_parent_physical_id(self, physical_id))
2043
0
    return;
2044
0
  g_ptr_array_add(priv->parent_physical_ids, g_strdup(physical_id));
2045
0
}
2046
2047
/**
2048
 * fu_device_get_parent_backend_ids:
2049
 * @self: a #FuDevice
2050
 *
2051
 * Gets any parent device IDs. If a device is added to the daemon that matches
2052
 * the physical ID added from fu_device_add_parent_backend_id() then this
2053
 * device is marked the parent of @self.
2054
 *
2055
 * Returns: (transfer none) (element-type utf8) (nullable): a list of IDs
2056
 *
2057
 * Since: 1.9.7
2058
 **/
2059
GPtrArray *
2060
fu_device_get_parent_backend_ids(FuDevice *self)
2061
0
{
2062
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
2063
0
  g_return_val_if_fail(FU_IS_DEVICE(self), NULL);
2064
0
  return priv->parent_backend_ids;
2065
0
}
2066
2067
/**
2068
 * fu_device_has_parent_backend_id:
2069
 * @self: a #FuDevice
2070
 * @backend_id: a device physical ID
2071
 *
2072
 * Searches the list of parent IDs for a string match.
2073
 *
2074
 * Returns: %TRUE if the parent ID exists
2075
 *
2076
 * Since: 1.9.7
2077
 **/
2078
gboolean
2079
fu_device_has_parent_backend_id(FuDevice *self, const gchar *backend_id)
2080
0
{
2081
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
2082
2083
0
  g_return_val_if_fail(FU_IS_DEVICE(self), FALSE);
2084
0
  g_return_val_if_fail(backend_id != NULL, FALSE);
2085
2086
0
  if (priv->parent_backend_ids == NULL)
2087
0
    return FALSE;
2088
0
  for (guint i = 0; i < priv->parent_backend_ids->len; i++) {
2089
0
    const gchar *tmp = g_ptr_array_index(priv->parent_backend_ids, i);
2090
0
    if (g_strcmp0(tmp, backend_id) == 0)
2091
0
      return TRUE;
2092
0
  }
2093
0
  return FALSE;
2094
0
}
2095
2096
/**
2097
 * fu_device_add_parent_backend_id:
2098
 * @self: a #FuDevice
2099
 * @backend_id: a device physical ID
2100
 *
2101
 * Sets any parent device using the physical ID. An parent device is logically
2102
 * linked to the primary device in some way and can be added before or after @self.
2103
 *
2104
 * The IDs are searched in order, and so the order of adding IDs may be
2105
 * important if more than one parent device might match.
2106
 *
2107
 * Since: 1.9.7
2108
 **/
2109
void
2110
fu_device_add_parent_backend_id(FuDevice *self, const gchar *backend_id)
2111
0
{
2112
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
2113
2114
0
  g_return_if_fail(FU_IS_DEVICE(self));
2115
0
  g_return_if_fail(backend_id != NULL);
2116
2117
  /* ensure exists */
2118
0
  if (priv->parent_backend_ids == NULL)
2119
0
    priv->parent_backend_ids = g_ptr_array_new_with_free_func(g_free);
2120
2121
  /* already present */
2122
0
  if (fu_device_has_parent_backend_id(self, backend_id))
2123
0
    return;
2124
0
  g_ptr_array_add(priv->parent_backend_ids, g_strdup(backend_id));
2125
0
}
2126
2127
static gboolean
2128
fu_device_add_child_by_type_guid(FuDevice *self, GType type, const gchar *guid, GError **error)
2129
0
{
2130
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
2131
0
  g_autoptr(FuDevice) child = NULL;
2132
0
  child = g_object_new(type, "context", priv->ctx, "logical-id", guid, NULL);
2133
0
  fu_device_add_instance_id(child, guid);
2134
0
  fu_device_incorporate(child, self, FU_DEVICE_INCORPORATE_FLAG_PHYSICAL_ID);
2135
0
  if (!fu_device_ensure_id(self, error))
2136
0
    return FALSE;
2137
0
  if (!fu_device_probe(child, error))
2138
0
    return FALSE;
2139
0
  fu_device_convert_instance_ids(child);
2140
0
  fu_device_add_child(self, child);
2141
0
  return TRUE;
2142
0
}
2143
2144
static gboolean
2145
fu_device_add_child_by_kv(FuDevice *self, const gchar *str, GError **error)
2146
0
{
2147
0
  g_auto(GStrv) split = g_strsplit(str, "|", -1);
2148
2149
  /* type same as parent */
2150
0
  if (g_strv_length(split) == 1) {
2151
0
    return fu_device_add_child_by_type_guid(self, G_OBJECT_TYPE(self), split[1], error);
2152
0
  }
2153
2154
  /* type specified */
2155
0
  if (g_strv_length(split) == 2) {
2156
0
    GType devtype = g_type_from_name(split[0]);
2157
0
    if (devtype == 0) {
2158
0
      g_set_error_literal(error,
2159
0
              FWUPD_ERROR,
2160
0
              FWUPD_ERROR_NOT_FOUND,
2161
0
              "no GType registered");
2162
0
      return FALSE;
2163
0
    }
2164
0
    return fu_device_add_child_by_type_guid(self, devtype, split[1], error);
2165
0
  }
2166
2167
  /* more than one '|' */
2168
0
  g_set_error_literal(error,
2169
0
          FWUPD_ERROR,
2170
0
          FWUPD_ERROR_NOT_FOUND,
2171
0
          "unable to add parse child section");
2172
0
  return FALSE;
2173
0
}
2174
2175
static gboolean
2176
fu_device_set_quirk_inhibit_section(FuDevice *self, const gchar *value, GError **error)
2177
0
{
2178
0
  g_auto(GStrv) sections = NULL;
2179
2180
0
  g_return_val_if_fail(value != NULL, FALSE);
2181
2182
  /* sanity check */
2183
0
  sections = g_strsplit(value, ":", -1);
2184
0
  if (g_strv_length(sections) != 2) {
2185
0
    g_set_error_literal(error,
2186
0
            FWUPD_ERROR,
2187
0
            FWUPD_ERROR_NOT_SUPPORTED,
2188
0
            "quirk key not supported, expected k1:v1[,k2:v2][,k3:]");
2189
0
    return FALSE;
2190
0
  }
2191
2192
  /* allow empty string to unset quirk */
2193
0
  if (g_strcmp0(sections[1], "") != 0)
2194
0
    fu_device_inhibit(self, sections[0], sections[1]);
2195
0
  else
2196
0
    fu_device_uninhibit(self, sections[0]);
2197
2198
  /* success */
2199
0
  return TRUE;
2200
0
}
2201
2202
/**
2203
 * fu_device_set_quirk_kv:
2204
 * @self: a #FuDevice
2205
 * @key: a string key
2206
 * @value: a string value
2207
 * @source: a #FuContextQuirkSource, e.g. %FU_CONTEXT_QUIRK_SOURCE_DB
2208
 * @error: (nullable): optional return location for an error
2209
 *
2210
 * Sets a specific quirk on the device.
2211
 *
2212
 * Returns: %TRUE on success
2213
 *
2214
 * Since: 2.0.2
2215
 **/
2216
gboolean
2217
fu_device_set_quirk_kv(FuDevice *self,
2218
           const gchar *key,
2219
           const gchar *value,
2220
           FuContextQuirkSource source,
2221
           GError **error)
2222
0
{
2223
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
2224
0
  FuDeviceClass *device_class = FU_DEVICE_GET_CLASS(self);
2225
0
  guint64 tmp;
2226
2227
0
  g_return_val_if_fail(FU_IS_DEVICE(self), FALSE);
2228
0
  g_return_val_if_fail(key != NULL, FALSE);
2229
0
  g_return_val_if_fail(value != NULL, FALSE);
2230
0
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
2231
2232
0
  if (g_strcmp0(key, FU_QUIRKS_PLUGIN) == 0) {
2233
0
    g_auto(GStrv) sections = g_strsplit(value, ",", -1);
2234
0
    for (guint i = 0; sections[i] != NULL; i++)
2235
0
      fu_device_add_possible_plugin(self, sections[i]);
2236
0
    return TRUE;
2237
0
  }
2238
0
  if (g_strcmp0(key, FU_QUIRKS_FLAGS) == 0) {
2239
0
    fu_device_set_custom_flags(self, value);
2240
0
    return TRUE;
2241
0
  }
2242
0
  if (g_strcmp0(key, FU_QUIRKS_NAME) == 0) {
2243
0
    if (fu_device_get_name(self) != NULL && source >= FU_CONTEXT_QUIRK_SOURCE_DB)
2244
0
      return TRUE;
2245
0
    fu_device_set_name(self, value);
2246
0
    return TRUE;
2247
0
  }
2248
0
  if (g_strcmp0(key, FU_QUIRKS_SUMMARY) == 0) {
2249
0
    fu_device_set_summary(self, value);
2250
0
    return TRUE;
2251
0
  }
2252
0
  if (g_strcmp0(key, FU_QUIRKS_BRANCH) == 0) {
2253
0
    fu_device_set_branch(self, value);
2254
0
    return TRUE;
2255
0
  }
2256
0
  if (g_strcmp0(key, FU_QUIRKS_VENDOR) == 0) {
2257
0
    if (fu_device_get_vendor(self) != NULL && source >= FU_CONTEXT_QUIRK_SOURCE_DB)
2258
0
      return TRUE;
2259
0
    fu_device_set_vendor(self, value);
2260
0
    return TRUE;
2261
0
  }
2262
0
  if (g_strcmp0(key, FU_QUIRKS_VENDOR_ID) == 0) {
2263
0
    g_auto(GStrv) sections = g_strsplit(value, ",", -1);
2264
0
    for (guint i = 0; sections[i] != NULL; i++)
2265
0
      fu_device_add_vendor_id(self, sections[i]);
2266
0
    return TRUE;
2267
0
  }
2268
0
  if (g_strcmp0(key, FU_QUIRKS_PROTOCOL) == 0) {
2269
0
    g_auto(GStrv) sections = g_strsplit(value, ",", -1);
2270
0
    for (guint i = 0; sections[i] != NULL; i++)
2271
0
      fu_device_add_protocol(self, sections[i]);
2272
0
    return TRUE;
2273
0
  }
2274
0
  if (g_strcmp0(key, FU_QUIRKS_ISSUE) == 0) {
2275
0
    g_auto(GStrv) sections = g_strsplit(value, ",", -1);
2276
0
    for (guint i = 0; sections[i] != NULL; i++)
2277
0
      fu_device_add_issue(self, sections[i]);
2278
0
    return TRUE;
2279
0
  }
2280
0
  if (g_strcmp0(key, FU_QUIRKS_VERSION) == 0) {
2281
0
    fu_device_set_version(self, value);
2282
0
    return TRUE;
2283
0
  }
2284
0
  if (g_strcmp0(key, FU_QUIRKS_UPDATE_MESSAGE) == 0) {
2285
0
    fu_device_set_update_message(self, value);
2286
0
    return TRUE;
2287
0
  }
2288
0
  if (g_strcmp0(key, FU_QUIRKS_UPDATE_IMAGE) == 0) {
2289
0
    fu_device_set_update_image(self, value);
2290
0
    return TRUE;
2291
0
  }
2292
0
  if (g_strcmp0(key, FU_QUIRKS_ICON) == 0) {
2293
0
    g_auto(GStrv) sections = g_strsplit(value, ",", -1);
2294
0
    if (fu_device_get_icons(self)->len > 0 &&
2295
0
        source >= FU_CONTEXT_QUIRK_SOURCE_FALLBACK)
2296
0
      return TRUE;
2297
0
    for (guint i = 0; sections[i] != NULL; i++)
2298
0
      fu_device_add_icon(self, sections[i]);
2299
0
    return TRUE;
2300
0
  }
2301
0
  if (g_strcmp0(key, FU_QUIRKS_GUID) == 0) {
2302
0
    g_auto(GStrv) sections = g_strsplit(value, ",", -1);
2303
0
    for (guint i = 0; sections[i] != NULL; i++) {
2304
0
      fu_device_add_instance_id_full(self,
2305
0
                   sections[i],
2306
0
                   FU_DEVICE_INSTANCE_FLAG_VISIBLE |
2307
0
                 FU_DEVICE_INSTANCE_FLAG_QUIRKS);
2308
0
    }
2309
0
    return TRUE;
2310
0
  }
2311
0
  if (g_strcmp0(key, FU_QUIRKS_GUID_QUIRK) == 0) {
2312
0
    g_auto(GStrv) sections = g_strsplit(value, ",", -1);
2313
0
    for (guint i = 0; sections[i] != NULL; i++) {
2314
0
      fu_device_add_instance_id_full(self,
2315
0
                   sections[i],
2316
0
                   FU_DEVICE_INSTANCE_FLAG_QUIRKS);
2317
0
    }
2318
0
    return TRUE;
2319
0
  }
2320
0
  if (g_strcmp0(key, FU_QUIRKS_COUNTERPART_GUID) == 0) {
2321
0
    g_auto(GStrv) sections = g_strsplit(value, ",", -1);
2322
0
    for (guint i = 0; sections[i] != NULL; i++) {
2323
0
      fu_device_add_instance_id_full(self,
2324
0
                   sections[i],
2325
0
                   FU_DEVICE_INSTANCE_FLAG_COUNTERPART);
2326
0
    }
2327
0
    return TRUE;
2328
0
  }
2329
0
  if (g_strcmp0(key, FU_QUIRKS_PARENT_GUID) == 0) {
2330
0
    g_auto(GStrv) sections = g_strsplit(value, ",", -1);
2331
0
    for (guint i = 0; sections[i] != NULL; i++)
2332
0
      fu_device_add_parent_guid(self, sections[i]);
2333
0
    return TRUE;
2334
0
  }
2335
0
  if (g_strcmp0(key, FU_QUIRKS_PROXY_GUID) == 0) {
2336
0
    fu_device_set_proxy_guid(self, value);
2337
0
    return TRUE;
2338
0
  }
2339
0
  if (g_strcmp0(key, FU_QUIRKS_FIRMWARE_SIZE_MIN) == 0) {
2340
0
    if (!fu_strtoull(value, &tmp, 0, G_MAXUINT64, FU_INTEGER_BASE_AUTO, error))
2341
0
      return FALSE;
2342
0
    fu_device_set_firmware_size_min(self, tmp);
2343
0
    return TRUE;
2344
0
  }
2345
0
  if (g_strcmp0(key, FU_QUIRKS_FIRMWARE_SIZE_MAX) == 0) {
2346
0
    if (!fu_strtoull(value, &tmp, 0, G_MAXUINT64, FU_INTEGER_BASE_AUTO, error))
2347
0
      return FALSE;
2348
0
    fu_device_set_firmware_size_max(self, tmp);
2349
0
    return TRUE;
2350
0
  }
2351
0
  if (g_strcmp0(key, FU_QUIRKS_FIRMWARE_SIZE) == 0) {
2352
0
    if (!fu_strtoull(value, &tmp, 0, G_MAXUINT64, FU_INTEGER_BASE_AUTO, error))
2353
0
      return FALSE;
2354
0
    fu_device_set_firmware_size(self, tmp);
2355
0
    return TRUE;
2356
0
  }
2357
0
  if (g_strcmp0(key, FU_QUIRKS_INSTALL_DURATION) == 0) {
2358
0
    if (!fu_strtoull(value, &tmp, 0, 60 * 60 * 24, FU_INTEGER_BASE_AUTO, error))
2359
0
      return FALSE;
2360
0
    fu_device_set_install_duration(self, tmp);
2361
0
    return TRUE;
2362
0
  }
2363
0
  if (g_strcmp0(key, FU_QUIRKS_PRIORITY) == 0) {
2364
0
    if (!fu_strtoull(value, &tmp, 0, G_MAXUINT8, FU_INTEGER_BASE_AUTO, error))
2365
0
      return FALSE;
2366
0
    fu_device_set_priority(self, tmp);
2367
0
    return TRUE;
2368
0
  }
2369
0
  if (g_strcmp0(key, FU_QUIRKS_BATTERY_THRESHOLD) == 0) {
2370
0
    if (!fu_strtoull(value, &tmp, 0, 100, FU_INTEGER_BASE_AUTO, error))
2371
0
      return FALSE;
2372
0
    fu_device_set_battery_threshold(self, tmp);
2373
0
    return TRUE;
2374
0
  }
2375
0
  if (g_strcmp0(key, FU_QUIRKS_REMOVE_DELAY) == 0) {
2376
0
    if (!fu_strtoull(value, &tmp, 0, G_MAXUINT, FU_INTEGER_BASE_AUTO, error))
2377
0
      return FALSE;
2378
0
    fu_device_set_remove_delay(self, tmp);
2379
0
    return TRUE;
2380
0
  }
2381
0
  if (g_strcmp0(key, FU_QUIRKS_ACQUIESCE_DELAY) == 0) {
2382
0
    if (!fu_strtoull(value, &tmp, 0, G_MAXUINT, FU_INTEGER_BASE_AUTO, error))
2383
0
      return FALSE;
2384
0
    fu_device_set_acquiesce_delay(self, tmp);
2385
0
    return TRUE;
2386
0
  }
2387
0
  if (g_strcmp0(key, FU_QUIRKS_VERSION_FORMAT) == 0) {
2388
0
    fu_device_set_version_format(self, fwupd_version_format_from_string(value));
2389
0
    return TRUE;
2390
0
  }
2391
0
  if (g_strcmp0(key, FU_QUIRKS_INHIBIT) == 0) {
2392
0
    g_auto(GStrv) sections = g_strsplit(value, ",", -1);
2393
0
    for (guint i = 0; sections[i] != NULL; i++) {
2394
0
      if (!fu_device_set_quirk_inhibit_section(self, sections[i], error))
2395
0
        return FALSE;
2396
0
    }
2397
0
    return TRUE;
2398
0
  }
2399
0
  if (g_strcmp0(key, FU_QUIRKS_GTYPE) == 0) {
2400
0
    if (priv->specialized_gtype != G_TYPE_INVALID) {
2401
0
      g_debug("already set GType to %s, ignoring %s",
2402
0
        g_type_name(priv->specialized_gtype),
2403
0
        value);
2404
0
      return TRUE;
2405
0
    }
2406
0
    priv->specialized_gtype = g_type_from_name(value);
2407
0
    if (priv->specialized_gtype == G_TYPE_INVALID) {
2408
0
      g_set_error(error,
2409
0
            FWUPD_ERROR,
2410
0
            FWUPD_ERROR_NOT_SUPPORTED,
2411
0
            "unknown GType name %s",
2412
0
            value);
2413
0
      return FALSE;
2414
0
    }
2415
0
    return TRUE;
2416
0
  }
2417
0
  if (g_strcmp0(key, FU_QUIRKS_PROXY_GTYPE) == 0) {
2418
0
    if (priv->proxy_gtype != G_TYPE_INVALID) {
2419
0
      g_debug("already set proxy GType to %s, ignoring %s",
2420
0
        g_type_name(priv->proxy_gtype),
2421
0
        value);
2422
0
      return TRUE;
2423
0
    }
2424
0
    priv->proxy_gtype = g_type_from_name(value);
2425
0
    if (priv->proxy_gtype == G_TYPE_INVALID) {
2426
0
      g_set_error(error,
2427
0
            FWUPD_ERROR,
2428
0
            FWUPD_ERROR_NOT_SUPPORTED,
2429
0
            "unknown GType name %s",
2430
0
            value);
2431
0
      return FALSE;
2432
0
    }
2433
0
    return TRUE;
2434
0
  }
2435
0
  if (g_strcmp0(key, FU_QUIRKS_FIRMWARE_GTYPE) == 0) {
2436
0
    if (priv->firmware_gtype != G_TYPE_INVALID) {
2437
0
      g_debug("already set firmware GType to %s, ignoring %s",
2438
0
        g_type_name(priv->firmware_gtype),
2439
0
        value);
2440
0
      return TRUE;
2441
0
    }
2442
0
    priv->firmware_gtype = g_type_from_name(value);
2443
0
    if (priv->firmware_gtype == G_TYPE_INVALID) {
2444
0
      g_set_error(error,
2445
0
            FWUPD_ERROR,
2446
0
            FWUPD_ERROR_NOT_SUPPORTED,
2447
0
            "unknown GType name %s",
2448
0
            value);
2449
0
      return FALSE;
2450
0
    }
2451
0
    return TRUE;
2452
0
  }
2453
0
  if (g_strcmp0(key, FU_QUIRKS_CHILDREN) == 0) {
2454
0
    g_auto(GStrv) sections = g_strsplit(value, ",", -1);
2455
0
    for (guint i = 0; sections[i] != NULL; i++) {
2456
0
      if (!fu_device_add_child_by_kv(self, sections[i], error))
2457
0
        return FALSE;
2458
0
    }
2459
0
    return TRUE;
2460
0
  }
2461
2462
  /* optional device-specific method */
2463
0
  if (device_class->set_quirk_kv != NULL)
2464
0
    return device_class->set_quirk_kv(self, key, value, error);
2465
2466
  /* failed */
2467
0
  g_set_error_literal(error,
2468
0
          FWUPD_ERROR,
2469
0
          FWUPD_ERROR_NOT_SUPPORTED,
2470
0
          "quirk key not supported");
2471
0
  return FALSE;
2472
0
}
2473
2474
/**
2475
 * fu_device_get_specialized_gtype:
2476
 * @self: a #FuDevice
2477
 *
2478
 * Gets the specialized type of the device
2479
 *
2480
 * Returns:#GType
2481
 *
2482
 * Since: 1.3.3
2483
 **/
2484
GType
2485
fu_device_get_specialized_gtype(FuDevice *self)
2486
0
{
2487
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
2488
0
  return priv->specialized_gtype;
2489
0
}
2490
2491
/**
2492
 * fu_device_set_specialized_gtype:
2493
 * @self: a #FuDevice
2494
 * @gtype: a #GType
2495
 *
2496
 * Sets the specialized type of the device
2497
 *
2498
 * Since: 2.0.0
2499
 **/
2500
void
2501
fu_device_set_specialized_gtype(FuDevice *self, GType gtype)
2502
0
{
2503
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
2504
0
  g_return_if_fail(FU_IS_DEVICE(self));
2505
0
  g_return_if_fail(gtype != G_TYPE_INVALID);
2506
0
  priv->specialized_gtype = gtype;
2507
0
}
2508
2509
/**
2510
 * fu_device_get_proxy_gtype:
2511
 * @self: a #FuDevice
2512
 *
2513
 * Gets the specialized type of the device
2514
 *
2515
 * Returns:#GType
2516
 *
2517
 * Since: 1.9.15
2518
 **/
2519
GType
2520
fu_device_get_proxy_gtype(FuDevice *self)
2521
0
{
2522
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
2523
0
  return priv->proxy_gtype;
2524
0
}
2525
2526
/**
2527
 * fu_device_set_proxy_gtype:
2528
 * @self: a #FuDevice
2529
 * @gtype: a #GType
2530
 *
2531
 * Sets the specialized type of the device
2532
 *
2533
 * Since: 1.9.15
2534
 **/
2535
void
2536
fu_device_set_proxy_gtype(FuDevice *self, GType gtype)
2537
0
{
2538
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
2539
0
  g_return_if_fail(FU_IS_DEVICE(self));
2540
0
  g_return_if_fail(gtype != G_TYPE_INVALID);
2541
0
  priv->proxy_gtype = gtype;
2542
0
}
2543
2544
/**
2545
 * fu_device_get_firmware_gtype:
2546
 * @self: a #FuDevice
2547
 *
2548
 * Gets the default firmware type for the device.
2549
 *
2550
 * Returns: #GType
2551
 *
2552
 * Since: 1.7.2
2553
 **/
2554
GType
2555
fu_device_get_firmware_gtype(FuDevice *self)
2556
0
{
2557
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
2558
0
  g_return_val_if_fail(FU_IS_DEVICE(self), G_TYPE_INVALID);
2559
0
  return priv->firmware_gtype;
2560
0
}
2561
2562
/**
2563
 * fu_device_set_firmware_gtype:
2564
 * @self: a #FuDevice
2565
 * @firmware_gtype: a #GType
2566
 *
2567
 * Sets the default firmware type for the device.
2568
 *
2569
 * Since: 1.7.2
2570
 **/
2571
void
2572
fu_device_set_firmware_gtype(FuDevice *self, GType firmware_gtype)
2573
0
{
2574
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
2575
0
  priv->firmware_gtype = firmware_gtype;
2576
0
}
2577
2578
static void
2579
fu_device_quirks_iter_cb(FuContext *ctx,
2580
       const gchar *key,
2581
       const gchar *value,
2582
       FuContextQuirkSource source,
2583
       gpointer user_data)
2584
0
{
2585
0
  FuDevice *self = FU_DEVICE(user_data);
2586
0
  g_autoptr(GError) error = NULL;
2587
0
  if (!fu_device_set_quirk_kv(self, key, value, source, &error)) {
2588
0
    if (!g_error_matches(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED))
2589
0
      g_warning("failed to set quirk key %s=%s: %s", key, value, error->message);
2590
0
  }
2591
0
}
2592
2593
static void
2594
fu_device_add_guid_quirks(FuDevice *self, const gchar *guid)
2595
0
{
2596
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
2597
2598
0
  g_return_if_fail(FU_IS_DEVICE(self));
2599
0
  g_return_if_fail(guid != NULL);
2600
2601
0
  if (priv->ctx == NULL) {
2602
0
    g_autofree gchar *str = fu_device_to_string(self);
2603
0
    g_critical("no FuContext assigned for %s", str);
2604
0
    return;
2605
0
  }
2606
2607
  /* run the query */
2608
0
  fu_context_lookup_quirk_by_id_iter(priv->ctx, guid, NULL, fu_device_quirks_iter_cb, self);
2609
0
}
2610
2611
/**
2612
 * fu_device_set_firmware_size:
2613
 * @self: a #FuDevice
2614
 * @size: Size in bytes
2615
 *
2616
 * Sets the exact allowed size of the firmware blob.
2617
 *
2618
 * Since: 1.2.6
2619
 **/
2620
void
2621
fu_device_set_firmware_size(FuDevice *self, guint64 size)
2622
0
{
2623
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
2624
0
  g_return_if_fail(FU_IS_DEVICE(self));
2625
0
  priv->size_min = size;
2626
0
  priv->size_max = size;
2627
0
}
2628
2629
/**
2630
 * fu_device_set_firmware_size_min:
2631
 * @self: a #FuDevice
2632
 * @size_min: Size in bytes
2633
 *
2634
 * Sets the minimum allowed size of the firmware blob.
2635
 *
2636
 * Since: 1.1.2
2637
 **/
2638
void
2639
fu_device_set_firmware_size_min(FuDevice *self, guint64 size_min)
2640
0
{
2641
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
2642
0
  g_return_if_fail(FU_IS_DEVICE(self));
2643
0
  priv->size_min = size_min;
2644
0
}
2645
2646
/**
2647
 * fu_device_set_firmware_size_max:
2648
 * @self: a #FuDevice
2649
 * @size_max: Size in bytes
2650
 *
2651
 * Sets the maximum allowed size of the firmware blob.
2652
 *
2653
 * Since: 1.1.2
2654
 **/
2655
void
2656
fu_device_set_firmware_size_max(FuDevice *self, guint64 size_max)
2657
0
{
2658
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
2659
0
  g_return_if_fail(FU_IS_DEVICE(self));
2660
0
  priv->size_max = size_max;
2661
0
}
2662
2663
/**
2664
 * fu_device_get_firmware_size_min:
2665
 * @self: a #FuDevice
2666
 *
2667
 * Gets the minimum size of the firmware blob.
2668
 *
2669
 * Returns: Size in bytes, or 0 if unset
2670
 *
2671
 * Since: 1.2.6
2672
 **/
2673
guint64
2674
fu_device_get_firmware_size_min(FuDevice *self)
2675
0
{
2676
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
2677
0
  g_return_val_if_fail(FU_IS_DEVICE(self), 0);
2678
0
  return priv->size_min;
2679
0
}
2680
2681
/**
2682
 * fu_device_get_firmware_size_max:
2683
 * @self: a #FuDevice
2684
 *
2685
 * Gets the maximum size of the firmware blob.
2686
 *
2687
 * Returns: Size in bytes, or 0 if unset
2688
 *
2689
 * Since: 1.2.6
2690
 **/
2691
guint64
2692
fu_device_get_firmware_size_max(FuDevice *self)
2693
0
{
2694
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
2695
0
  g_return_val_if_fail(FU_IS_DEVICE(self), 0);
2696
0
  return priv->size_max;
2697
0
}
2698
2699
/**
2700
 * fu_device_get_required_free:
2701
 * @self: a #FuDevice
2702
 *
2703
 * Returns the required amount of free firmware space.
2704
 *
2705
 * Returns: size in bytes
2706
 *
2707
 * Since: 2.0.12
2708
 **/
2709
guint64
2710
fu_device_get_required_free(FuDevice *self)
2711
0
{
2712
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
2713
0
  g_return_val_if_fail(FU_IS_DEVICE(self), 0);
2714
0
  return priv->required_free;
2715
0
}
2716
2717
/**
2718
 * fu_device_set_required_free:
2719
 * @self: a #FuDevice
2720
 * @required_free: size in bytes
2721
 *
2722
 * Sets the required amount of free firmware size.
2723
 *
2724
 * NOTE: What we really want to do for EFI devices is check if a *contiguous* block of the right
2725
 * size can be written, but on most machines this causes an SMI which causes all cores to halt.
2726
 * On my desktop this causes **ALL** CPU processes to stop for ~1s, which is clearly unacceptable
2727
 * at every boot. Instead, check for free space at least as big as needed, plus a little extra.
2728
 *
2729
 * Since: 2.0.12
2730
 **/
2731
void
2732
fu_device_set_required_free(FuDevice *self, guint64 required_free)
2733
0
{
2734
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
2735
0
  g_return_if_fail(FU_IS_DEVICE(self));
2736
0
  if (priv->required_free == required_free)
2737
0
    return;
2738
0
  priv->required_free = required_free;
2739
0
  g_object_notify(G_OBJECT(self), "required-free");
2740
0
}
2741
2742
/**
2743
 * fu_device_has_guid:
2744
 * @self: a #FuDevice
2745
 * @guid: a GUID, e.g. `WacomAES`
2746
 *
2747
 * Finds out if the device has a specific GUID.
2748
 *
2749
 * Returns: %TRUE if the GUID is found
2750
 *
2751
 * Since: 1.2.2
2752
 **/
2753
gboolean
2754
fu_device_has_guid(FuDevice *self, const gchar *guid)
2755
0
{
2756
0
  g_return_val_if_fail(FU_IS_DEVICE(self), FALSE);
2757
0
  g_return_val_if_fail(guid != NULL, FALSE);
2758
2759
  /* make valid */
2760
0
  if (!fwupd_guid_is_valid(guid)) {
2761
0
    g_autofree gchar *tmp = fwupd_guid_hash_string(guid);
2762
0
    return fwupd_device_has_guid(FWUPD_DEVICE(self), tmp);
2763
0
  }
2764
2765
  /* already valid */
2766
0
  return fwupd_device_has_guid(FWUPD_DEVICE(self), guid);
2767
0
}
2768
2769
static void
2770
fu_device_instance_id_free(FuDeviceInstanceIdItem *item)
2771
0
{
2772
0
  g_free(item->instance_id);
2773
0
  g_free(item->guid);
2774
0
  g_free(item);
2775
0
}
2776
2777
static FuDeviceInstanceIdItem *
2778
fu_device_get_instance_id(FuDevice *self, const gchar *instance_id)
2779
0
{
2780
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
2781
0
  if (priv->instance_ids == NULL)
2782
0
    return NULL;
2783
0
  for (guint i = 0; i < priv->instance_ids->len; i++) {
2784
0
    FuDeviceInstanceIdItem *item = g_ptr_array_index(priv->instance_ids, i);
2785
0
    if (g_strcmp0(instance_id, item->instance_id) == 0)
2786
0
      return item;
2787
0
    if (g_strcmp0(instance_id, item->guid) == 0)
2788
0
      return item;
2789
0
  }
2790
0
  return NULL;
2791
0
}
2792
2793
/**
2794
 * fu_device_has_instance_id:
2795
 * @self: a #FuDevice
2796
 * @instance_id: a instance ID, e.g. `WacomAES`
2797
 * @flags: instance ID flags
2798
 *
2799
 * Finds out if the device has this specific instance ID.
2800
 *
2801
 * NOTE: The instance IDs are only added to the actual base #FwupdDevice after
2802
 * fu_device_convert_instance_ids() has been called -- normally as part of `FuDevice->setup()`.
2803
 * This ensures that incorporating a baseclass to a target device with the flag
2804
 * %FU_DEVICE_PRIVATE_FLAG_NO_GENERIC_GUIDS does not have generic instance IDs added.
2805
 *
2806
 * Returns: %TRUE if the instance ID is found
2807
 *
2808
 * Since: 2.0.4
2809
 **/
2810
gboolean
2811
fu_device_has_instance_id(FuDevice *self, const gchar *instance_id, FuDeviceInstanceFlags flags)
2812
0
{
2813
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
2814
2815
0
  g_return_val_if_fail(FU_IS_DEVICE(self), FALSE);
2816
0
  g_return_val_if_fail(instance_id != NULL, FALSE);
2817
2818
0
  for (guint i = 0; priv->instance_ids != NULL && i < priv->instance_ids->len; i++) {
2819
0
    FuDeviceInstanceIdItem *item = g_ptr_array_index(priv->instance_ids, i);
2820
0
    if ((item->flags & flags) == 0)
2821
0
      continue;
2822
0
    if (g_strcmp0(item->instance_id, instance_id) == 0 ||
2823
0
        g_strcmp0(item->guid, instance_id) == 0)
2824
0
      return TRUE;
2825
0
  }
2826
0
  return FALSE;
2827
0
}
2828
2829
/**
2830
 * fu_device_add_instance_id_full:
2831
 * @self: a #FuDevice
2832
 * @instance_id: a instance ID or GUID, e.g. `WacomAES`
2833
 * @flags: instance ID flags
2834
 *
2835
 * Adds an instance ID or GUID with all parameters set.
2836
 *
2837
 * A counterpart GUID is the GUID of the same device in bootloader or runtime mode,
2838
 * if they have a different device PCI or USB ID.
2839
 *
2840
 * Since: 1.2.9
2841
 **/
2842
void
2843
fu_device_add_instance_id_full(FuDevice *self,
2844
             const gchar *instance_id,
2845
             FuDeviceInstanceFlags flags)
2846
0
{
2847
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
2848
0
  FuDeviceInstanceIdItem *item;
2849
2850
0
  g_return_if_fail(FU_IS_DEVICE(self));
2851
0
  g_return_if_fail(instance_id != NULL);
2852
2853
  /* some devices in recovery mode expect this to work */
2854
0
  if ((flags & FU_DEVICE_INSTANCE_FLAG_COUNTERPART) > 0 &&
2855
0
      fu_device_has_private_flag(self, FU_DEVICE_PRIVATE_FLAG_COUNTERPART_VISIBLE)) {
2856
0
    g_debug("making %s also visible", instance_id);
2857
0
    flags |= FU_DEVICE_INSTANCE_FLAG_VISIBLE;
2858
0
  }
2859
2860
  /* add to cache */
2861
0
  item = fu_device_get_instance_id(self, instance_id);
2862
0
  if (item != NULL) {
2863
0
    if ((item->flags & FU_DEVICE_INSTANCE_FLAG_QUIRKS) == 0 &&
2864
0
        (flags & FU_DEVICE_INSTANCE_FLAG_QUIRKS) > 0) {
2865
      /* visible -> visible+quirks */
2866
0
      fu_device_add_guid_quirks(self, item->guid);
2867
0
    }
2868
0
    item->flags |= flags;
2869
0
  } else {
2870
0
    item = g_new0(FuDeviceInstanceIdItem, 1);
2871
0
    if (fwupd_guid_is_valid(instance_id)) {
2872
0
      item->guid = g_strdup(instance_id);
2873
0
    } else {
2874
0
      item->instance_id = g_strdup(instance_id);
2875
0
      item->guid = fwupd_guid_hash_string(instance_id);
2876
0
    }
2877
0
    item->flags |= flags;
2878
0
    if (priv->instance_ids == NULL)
2879
0
      priv->instance_ids = g_ptr_array_new_with_free_func(
2880
0
          (GDestroyNotify)fu_device_instance_id_free);
2881
0
    g_ptr_array_add(priv->instance_ids, item);
2882
2883
    /* we want the quirks to match so the plugin is set */
2884
0
    if (flags & FU_DEVICE_INSTANCE_FLAG_QUIRKS)
2885
0
      fu_device_add_guid_quirks(self, item->guid);
2886
0
  }
2887
2888
  /* already done by ->setup(), so this must be ->registered() */
2889
0
  if (priv->done_setup) {
2890
0
    if (item->instance_id != NULL)
2891
0
      fwupd_device_add_instance_id(FWUPD_DEVICE(self), item->instance_id);
2892
0
    fwupd_device_add_guid(FWUPD_DEVICE(self), item->guid);
2893
0
  }
2894
0
}
2895
2896
/**
2897
 * fu_device_add_instance_id:
2898
 * @self: a #FuDevice
2899
 * @instance_id: the instance ID, e.g. `PCI\VEN_10EC&DEV_525A`
2900
 *
2901
 * Adds an visible, quirked, instance ID to the device.
2902
 *
2903
 * Since: 1.2.5
2904
 **/
2905
void
2906
fu_device_add_instance_id(FuDevice *self, const gchar *instance_id)
2907
0
{
2908
0
  g_return_if_fail(FU_IS_DEVICE(self));
2909
0
  g_return_if_fail(instance_id != NULL);
2910
0
  fu_device_add_instance_id_full(self,
2911
0
               instance_id,
2912
0
               FU_DEVICE_INSTANCE_FLAG_VISIBLE |
2913
0
             FU_DEVICE_INSTANCE_FLAG_QUIRKS);
2914
0
}
2915
2916
/**
2917
 * fu_device_get_counterpart_guids:
2918
 * @self: a #FuDevice
2919
 *
2920
 * Returns all the counterpart GUIDs.
2921
 *
2922
 * Returns: (transfer container) (element-type utf8): list of GUIDs
2923
 *
2924
 * Since: 1.9.21
2925
 **/
2926
GPtrArray *
2927
fu_device_get_counterpart_guids(FuDevice *self)
2928
0
{
2929
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
2930
0
  g_autoptr(GPtrArray) guids = g_ptr_array_new_with_free_func(g_free);
2931
2932
0
  g_return_val_if_fail(FU_IS_DEVICE(self), NULL);
2933
2934
0
  for (guint i = 0; priv->instance_ids != NULL && i < priv->instance_ids->len; i++) {
2935
0
    FuDeviceInstanceIdItem *item = g_ptr_array_index(priv->instance_ids, i);
2936
0
    if (item->flags & FU_DEVICE_INSTANCE_FLAG_COUNTERPART)
2937
0
      g_ptr_array_add(guids, g_strdup(item->guid));
2938
0
  }
2939
0
  return g_steal_pointer(&guids);
2940
0
}
2941
2942
/**
2943
 * fu_device_get_metadata:
2944
 * @self: a #FuDevice
2945
 * @key: the key
2946
 *
2947
 * Gets an item of metadata from the device.
2948
 *
2949
 * Returns: a string value, or %NULL for unfound.
2950
 *
2951
 * Since: 0.1.0
2952
 **/
2953
const gchar *
2954
fu_device_get_metadata(FuDevice *self, const gchar *key)
2955
0
{
2956
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
2957
0
  g_return_val_if_fail(FU_IS_DEVICE(self), NULL);
2958
0
  g_return_val_if_fail(key != NULL, NULL);
2959
0
  if (priv->metadata == NULL)
2960
0
    return NULL;
2961
0
  return g_hash_table_lookup(priv->metadata, key);
2962
0
}
2963
2964
/**
2965
 * fu_device_get_metadata_boolean:
2966
 * @self: a #FuDevice
2967
 * @key: the key
2968
 *
2969
 * Gets an item of metadata from the device.
2970
 *
2971
 * Returns: a boolean value, or %FALSE for unfound or failure to parse.
2972
 *
2973
 * Since: 0.9.7
2974
 **/
2975
gboolean
2976
fu_device_get_metadata_boolean(FuDevice *self, const gchar *key)
2977
0
{
2978
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
2979
0
  const gchar *tmp;
2980
2981
0
  g_return_val_if_fail(FU_IS_DEVICE(self), FALSE);
2982
0
  g_return_val_if_fail(key != NULL, FALSE);
2983
2984
0
  if (priv->metadata == NULL)
2985
0
    return FALSE;
2986
0
  tmp = g_hash_table_lookup(priv->metadata, key);
2987
0
  if (tmp == NULL)
2988
0
    return FALSE;
2989
0
  return g_strcmp0(tmp, "true") == 0;
2990
0
}
2991
2992
/**
2993
 * fu_device_get_metadata_integer:
2994
 * @self: a #FuDevice
2995
 * @key: the key
2996
 *
2997
 * Gets an item of metadata from the device.
2998
 *
2999
 * Returns: an integer value, or %G_MAXUINT for unfound or failure to parse.
3000
 *
3001
 * Since: 0.9.7
3002
 **/
3003
guint
3004
fu_device_get_metadata_integer(FuDevice *self, const gchar *key)
3005
0
{
3006
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
3007
0
  const gchar *tmp;
3008
0
  guint64 val = 0;
3009
0
  g_autoptr(GError) error_local = NULL;
3010
3011
0
  g_return_val_if_fail(FU_IS_DEVICE(self), G_MAXUINT);
3012
0
  g_return_val_if_fail(key != NULL, G_MAXUINT);
3013
3014
0
  if (priv->metadata == NULL)
3015
0
    return G_MAXUINT;
3016
0
  tmp = g_hash_table_lookup(priv->metadata, key);
3017
0
  if (tmp == NULL)
3018
0
    return G_MAXUINT;
3019
0
  if (!fu_strtoull(tmp, &val, 0, G_MAXUINT, FU_INTEGER_BASE_AUTO, &error_local)) {
3020
0
    g_warning("could not convert %s to integer: %s", tmp, error_local->message);
3021
0
    return G_MAXUINT;
3022
0
  }
3023
0
  return (guint)val;
3024
0
}
3025
3026
/**
3027
 * fu_device_remove_metadata:
3028
 * @self: a #FuDevice
3029
 * @key: the key
3030
 *
3031
 * Removes an item of metadata on the device.
3032
 *
3033
 * Since: 1.3.3
3034
 **/
3035
void
3036
fu_device_remove_metadata(FuDevice *self, const gchar *key)
3037
0
{
3038
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
3039
0
  g_return_if_fail(FU_IS_DEVICE(self));
3040
0
  g_return_if_fail(key != NULL);
3041
0
  if (priv->metadata == NULL)
3042
0
    return;
3043
0
  g_hash_table_remove(priv->metadata, key);
3044
0
}
3045
3046
/**
3047
 * fu_device_set_metadata:
3048
 * @self: a #FuDevice
3049
 * @key: the key
3050
 * @value: the string value
3051
 *
3052
 * Sets an item of metadata on the device.
3053
 *
3054
 * Since: 0.1.0
3055
 **/
3056
void
3057
fu_device_set_metadata(FuDevice *self, const gchar *key, const gchar *value)
3058
0
{
3059
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
3060
0
  g_return_if_fail(FU_IS_DEVICE(self));
3061
0
  g_return_if_fail(key != NULL);
3062
0
  g_return_if_fail(value != NULL);
3063
0
  if (priv->metadata == NULL)
3064
0
    priv->metadata = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
3065
0
  g_hash_table_insert(priv->metadata, g_strdup(key), g_strdup(value));
3066
0
}
3067
3068
/**
3069
 * fu_device_set_metadata_boolean:
3070
 * @self: a #FuDevice
3071
 * @key: the key
3072
 * @value: the boolean value
3073
 *
3074
 * Sets an item of metadata on the device. When @value is set to %TRUE
3075
 * the actual stored value is `true`.
3076
 *
3077
 * Since: 0.9.7
3078
 **/
3079
void
3080
fu_device_set_metadata_boolean(FuDevice *self, const gchar *key, gboolean value)
3081
0
{
3082
0
  g_return_if_fail(FU_IS_DEVICE(self));
3083
0
  g_return_if_fail(key != NULL);
3084
3085
0
  fu_device_set_metadata(self, key, value ? "true" : "false");
3086
0
}
3087
3088
/**
3089
 * fu_device_set_metadata_integer:
3090
 * @self: a #FuDevice
3091
 * @key: the key
3092
 * @value: the unsigned integer value
3093
 *
3094
 * Sets an item of metadata on the device. The integer is stored as a
3095
 * base-10 string internally.
3096
 *
3097
 * Since: 0.9.7
3098
 **/
3099
void
3100
fu_device_set_metadata_integer(FuDevice *self, const gchar *key, guint value)
3101
0
{
3102
0
  g_autofree gchar *tmp = g_strdup_printf("%u", value);
3103
3104
0
  g_return_if_fail(FU_IS_DEVICE(self));
3105
0
  g_return_if_fail(key != NULL);
3106
3107
0
  fu_device_set_metadata(self, key, tmp);
3108
0
}
3109
3110
/* ensure the name does not have the vendor name as the prefix */
3111
static void
3112
fu_device_fixup_vendor_name(FuDevice *self)
3113
0
{
3114
0
  const gchar *name = fu_device_get_name(self);
3115
0
  const gchar *vendor = fu_device_get_vendor(self);
3116
0
  if (name != NULL && vendor != NULL) {
3117
0
    g_autofree gchar *name_up = g_utf8_strup(name, -1);
3118
0
    g_autofree gchar *vendor_up = g_utf8_strup(vendor, -1);
3119
0
    if (g_strcmp0(name_up, vendor_up) == 0) {
3120
0
#ifndef SUPPORTED_BUILD
3121
0
      g_warning("name and vendor are the same for %s [%s]",
3122
0
          fu_device_get_name(self),
3123
0
          fu_device_get_id(self));
3124
0
#endif
3125
0
      return;
3126
0
    }
3127
0
    if (g_str_has_prefix(name_up, vendor_up)) {
3128
0
      gsize vendor_len = strlen(vendor);
3129
0
      g_autofree gchar *name1 = g_strdup(name + vendor_len);
3130
0
      g_autofree gchar *name2 = fu_strstrip(name1);
3131
0
      g_debug("removing vendor prefix of '%s' from '%s'", vendor, name);
3132
0
      fwupd_device_set_name(FWUPD_DEVICE(self), name2);
3133
0
    }
3134
0
  }
3135
0
}
3136
3137
/**
3138
 * fu_device_set_vendor:
3139
 * @self: a #FuDevice
3140
 * @vendor: a device vendor
3141
 *
3142
 * Sets the vendor name on the device.
3143
 *
3144
 * Since: 1.6.2
3145
 **/
3146
void
3147
fu_device_set_vendor(FuDevice *self, const gchar *vendor)
3148
0
{
3149
0
  g_autofree gchar *vendor_safe = NULL;
3150
3151
  /* trim any leading and trailing spaces */
3152
0
  if (vendor != NULL)
3153
0
    vendor_safe = fu_strstrip(vendor);
3154
3155
  /* proxy */
3156
0
  fwupd_device_set_vendor(FWUPD_DEVICE(self), vendor_safe);
3157
0
  fu_device_fixup_vendor_name(self);
3158
0
}
3159
3160
static gchar *
3161
fu_device_sanitize_name(const gchar *value)
3162
0
{
3163
0
  gboolean last_was_space = FALSE;
3164
0
  guint last_non_space = 0;
3165
0
  g_autoptr(GString) new = g_string_new(NULL);
3166
3167
  /* add each printable char with maximum of one whitespace char */
3168
0
  for (guint i = 0; value[i] != '\0'; i++) {
3169
0
    const gchar tmp = value[i];
3170
0
    if (!g_ascii_isprint(tmp))
3171
0
      continue;
3172
0
    if (g_ascii_isspace(tmp) || tmp == '_') {
3173
0
      if (new->len == 0)
3174
0
        continue;
3175
0
      if (last_was_space)
3176
0
        continue;
3177
0
      last_was_space = TRUE;
3178
0
      g_string_append_c(new, ' ');
3179
0
    } else {
3180
0
      last_was_space = FALSE;
3181
0
      g_string_append_c(new, tmp);
3182
0
      last_non_space = new->len;
3183
0
    }
3184
0
  }
3185
0
  g_string_truncate(new, last_non_space);
3186
0
  g_string_replace(new, "(TM)", "™", 0);
3187
0
  g_string_replace(new, "(R)", "", 0);
3188
0
  if (new->len == 0)
3189
0
    return NULL;
3190
0
  return g_string_free(g_steal_pointer(&new), FALSE);
3191
0
}
3192
3193
/**
3194
 * fu_device_set_name:
3195
 * @self: a #FuDevice
3196
 * @value: a device name
3197
 *
3198
 * Sets the name on the device. Any invalid parts will be converted or removed.
3199
 *
3200
 * Since: 0.7.1
3201
 **/
3202
void
3203
fu_device_set_name(FuDevice *self, const gchar *value)
3204
0
{
3205
0
  g_autofree gchar *value_safe = NULL;
3206
3207
0
  g_return_if_fail(FU_IS_DEVICE(self));
3208
0
  g_return_if_fail(value != NULL);
3209
3210
  /* overwriting? */
3211
0
  value_safe = fu_device_sanitize_name(value);
3212
0
  if (value_safe == NULL) {
3213
0
    g_info("ignoring name value: '%s'", value);
3214
0
    return;
3215
0
  }
3216
0
  if (g_strcmp0(value_safe, fu_device_get_name(self)) == 0)
3217
0
    return;
3218
3219
  /* changing */
3220
0
  if (fu_device_get_name(self) != NULL) {
3221
0
    const gchar *id = fu_device_get_id(self);
3222
0
    g_debug("%s device overwriting name value: %s->%s",
3223
0
      id != NULL ? id : "unknown",
3224
0
      fu_device_get_name(self),
3225
0
      value_safe);
3226
0
  }
3227
3228
0
  fwupd_device_set_name(FWUPD_DEVICE(self), value_safe);
3229
0
  fu_device_fixup_vendor_name(self);
3230
0
}
3231
3232
/**
3233
 * fu_device_set_id:
3234
 * @self: a #FuDevice
3235
 * @id: a string, e.g. `tbt-port1`
3236
 *
3237
 * Sets the ID on the device. The ID should represent the *connection* of the
3238
 * device, so that any similar device plugged into a different slot will
3239
 * have a different @id string.
3240
 *
3241
 * The @id will be converted to a SHA1 hash if required before the device is
3242
 * added to the daemon, and plugins should not assume that the ID that is set
3243
 * here is the same as what is returned by fu_device_get_id().
3244
 *
3245
 * Since: 0.7.1
3246
 **/
3247
void
3248
fu_device_set_id(FuDevice *self, const gchar *id)
3249
0
{
3250
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
3251
0
  GPtrArray *children;
3252
0
  g_autofree gchar *id_hash = NULL;
3253
0
  g_autofree gchar *id_hash_old = g_strdup(fwupd_device_get_id(FWUPD_DEVICE(self)));
3254
3255
0
  g_return_if_fail(FU_IS_DEVICE(self));
3256
0
  g_return_if_fail(id != NULL);
3257
3258
  /* allow sane device-id to be set directly */
3259
0
  if (fwupd_device_id_is_valid(id)) {
3260
0
    id_hash = g_strdup(id);
3261
0
  } else {
3262
0
    id_hash = g_compute_checksum_for_string(G_CHECKSUM_SHA1, id, -1);
3263
0
    g_debug("using %s for %s", id_hash, id);
3264
0
  }
3265
0
  fwupd_device_set_id(FWUPD_DEVICE(self), id_hash);
3266
0
  priv->device_id_valid = TRUE;
3267
3268
  /* ensure the parent ID is set */
3269
0
  children = fu_device_get_children(self);
3270
0
  for (guint i = 0; i < children->len; i++) {
3271
0
    FuDevice *devtmp = g_ptr_array_index(children, i);
3272
0
    fwupd_device_set_parent_id(FWUPD_DEVICE(devtmp), id_hash);
3273
3274
    /* update the composite ID of the child with the new ID if required; this will
3275
     * propagate to grandchildren and great-grandchildren as required */
3276
0
    if (id_hash_old != NULL &&
3277
0
        g_strcmp0(fu_device_get_composite_id(devtmp), id_hash_old) == 0)
3278
0
      fu_device_set_composite_id(devtmp, id_hash);
3279
0
  }
3280
0
}
3281
3282
/**
3283
 * fu_device_set_version_format:
3284
 * @self: a #FuDevice
3285
 * @fmt: the version format, e.g. %FWUPD_VERSION_FORMAT_PLAIN
3286
 *
3287
 * Sets the device version format.
3288
 *
3289
 * Since: 1.4.0
3290
 **/
3291
void
3292
fu_device_set_version_format(FuDevice *self, FwupdVersionFormat fmt)
3293
0
{
3294
0
  FuDeviceClass *device_class = FU_DEVICE_GET_CLASS(self);
3295
3296
  /* same */
3297
0
  if (fu_device_get_version_format(self) == fmt)
3298
0
    return;
3299
0
  if (fu_device_get_version_format(self) != FWUPD_VERSION_FORMAT_UNKNOWN) {
3300
0
    g_debug("changing verfmt for %s: %s->%s",
3301
0
      fu_device_get_id(self),
3302
0
      fwupd_version_format_to_string(fu_device_get_version_format(self)),
3303
0
      fwupd_version_format_to_string(fmt));
3304
0
  }
3305
0
  fwupd_device_set_version_format(FWUPD_DEVICE(self), fmt);
3306
3307
  /* convert this, now we know */
3308
0
  if (device_class->convert_version != NULL) {
3309
0
    if (fu_device_get_version_raw(self) != 0) {
3310
0
      g_autofree gchar *version =
3311
0
          device_class->convert_version(self, fu_device_get_version_raw(self));
3312
0
      fu_device_set_version(self, version);
3313
0
    }
3314
0
    if (fu_device_get_version_lowest_raw(self) != 0) {
3315
0
      g_autofree gchar *version =
3316
0
          device_class->convert_version(self,
3317
0
                fu_device_get_version_lowest_raw(self));
3318
0
      fu_device_set_version_lowest(self, version);
3319
0
    }
3320
0
  }
3321
0
}
3322
3323
/**
3324
 * fu_device_set_version:
3325
 * @self: a #FuDevice
3326
 * @version: (nullable): a string, e.g. `1.2.3`
3327
 *
3328
 * Sets the device version, sanitizing the string if required.
3329
 *
3330
 * Since: 1.2.9
3331
 **/
3332
void
3333
fu_device_set_version(FuDevice *self, const gchar *version)
3334
0
{
3335
0
  g_autofree gchar *version_safe = NULL;
3336
0
  g_autoptr(GError) error = NULL;
3337
3338
0
  g_return_if_fail(FU_IS_DEVICE(self));
3339
3340
  /* sanitize if required */
3341
0
  if (fu_device_has_private_flag(self, FU_DEVICE_PRIVATE_FLAG_ENSURE_SEMVER)) {
3342
0
    version_safe =
3343
0
        fu_version_ensure_semver(version, fu_device_get_version_format(self));
3344
0
    if (g_strcmp0(version, version_safe) != 0)
3345
0
      g_debug("converted '%s' to '%s'", version, version_safe);
3346
0
  } else {
3347
0
    version_safe = g_strdup(version);
3348
0
  }
3349
3350
  /* print a console warning for an invalid version, if semver */
3351
0
  if (version_safe != NULL &&
3352
0
      !fu_version_verify_format(version_safe, fu_device_get_version_format(self), &error))
3353
#ifdef SUPPORTED_BUILD
3354
    g_warning("%s", error->message);
3355
#else
3356
0
    g_critical("%s", error->message);
3357
0
#endif
3358
3359
  /* if different */
3360
0
  if (g_strcmp0(fu_device_get_version(self), version_safe) != 0) {
3361
0
    if (fu_device_get_version(self) != NULL) {
3362
0
      g_debug("changing version for %s: %s->%s",
3363
0
        fu_device_get_id(self),
3364
0
        fu_device_get_version(self),
3365
0
        version_safe);
3366
0
    }
3367
0
    fwupd_device_set_version(FWUPD_DEVICE(self), version_safe);
3368
0
  }
3369
0
}
3370
3371
/**
3372
 * fu_device_set_version_lowest:
3373
 * @self: a #FuDevice
3374
 * @version: (nullable): a string, e.g. `1.2.3`
3375
 *
3376
 * Sets the device lowest version, sanitizing the string if required.
3377
 *
3378
 * Since: 1.4.0
3379
 **/
3380
void
3381
fu_device_set_version_lowest(FuDevice *self, const gchar *version)
3382
0
{
3383
0
  g_autofree gchar *version_safe = NULL;
3384
0
  g_autoptr(GError) error = NULL;
3385
3386
0
  g_return_if_fail(FU_IS_DEVICE(self));
3387
3388
  /* sanitize if required */
3389
0
  if (fu_device_has_private_flag(self, FU_DEVICE_PRIVATE_FLAG_ENSURE_SEMVER)) {
3390
0
    version_safe =
3391
0
        fu_version_ensure_semver(version, fu_device_get_version_format(self));
3392
0
    if (g_strcmp0(version, version_safe) != 0)
3393
0
      g_debug("converted '%s' to '%s'", version, version_safe);
3394
0
  } else {
3395
0
    version_safe = g_strdup(version);
3396
0
  }
3397
3398
  /* print a console warning for an invalid version, if semver */
3399
0
  if (version_safe != NULL &&
3400
0
      !fu_version_verify_format(version_safe, fu_device_get_version_format(self), &error))
3401
#ifdef SUPPORTED_BUILD
3402
    g_warning("%s", error->message);
3403
#else
3404
0
    g_critical("%s", error->message);
3405
0
#endif
3406
3407
  /* if different */
3408
0
  if (g_strcmp0(fu_device_get_version_lowest(self), version_safe) != 0) {
3409
0
    if (fu_device_get_version_lowest(self) != NULL) {
3410
0
      g_debug("changing version lowest for %s: %s->%s",
3411
0
        fu_device_get_id(self),
3412
0
        fu_device_get_version_lowest(self),
3413
0
        version_safe);
3414
0
    }
3415
0
    fwupd_device_set_version_lowest(FWUPD_DEVICE(self), version_safe);
3416
0
  }
3417
0
}
3418
3419
/**
3420
 * fu_device_set_version_bootloader:
3421
 * @self: a #FuDevice
3422
 * @version: (nullable): a string, e.g. `1.2.3`
3423
 *
3424
 * Sets the device bootloader version, sanitizing the string if required.
3425
 *
3426
 * Since: 1.4.0
3427
 **/
3428
void
3429
fu_device_set_version_bootloader(FuDevice *self, const gchar *version)
3430
0
{
3431
0
  g_autofree gchar *version_safe = NULL;
3432
0
  g_autoptr(GError) error = NULL;
3433
3434
0
  g_return_if_fail(FU_IS_DEVICE(self));
3435
3436
  /* sanitize if required */
3437
0
  if (fu_device_has_private_flag(self, FU_DEVICE_PRIVATE_FLAG_ENSURE_SEMVER)) {
3438
0
    version_safe =
3439
0
        fu_version_ensure_semver(version, fu_device_get_version_format(self));
3440
0
    if (g_strcmp0(version, version_safe) != 0)
3441
0
      g_debug("converted '%s' to '%s'", version, version_safe);
3442
0
  } else {
3443
0
    version_safe = g_strdup(version);
3444
0
  }
3445
3446
  /* print a console warning for an invalid version, if semver */
3447
0
  if (version_safe != NULL &&
3448
0
      !fu_version_verify_format(version_safe, fu_device_get_version_format(self), &error))
3449
#ifdef SUPPORTED_BUILD
3450
    g_warning("%s", error->message);
3451
#else
3452
0
    g_critical("%s", error->message);
3453
0
#endif
3454
3455
  /* if different */
3456
0
  if (g_strcmp0(fu_device_get_version_bootloader(self), version_safe) != 0) {
3457
0
    if (fu_device_get_version_bootloader(self) != NULL) {
3458
0
      g_debug("changing version for %s: %s->%s",
3459
0
        fu_device_get_id(self),
3460
0
        fu_device_get_version_bootloader(self),
3461
0
        version_safe);
3462
0
    }
3463
0
    fwupd_device_set_version_bootloader(FWUPD_DEVICE(self), version_safe);
3464
0
  }
3465
0
}
3466
3467
/**
3468
 * fu_device_set_version_raw:
3469
 * @self: a #FuDevice
3470
 * @version_raw: an integer
3471
 *
3472
 * Sets the raw device version from a integer value and the device version format.
3473
 *
3474
 * Since: 1.9.8
3475
 **/
3476
void
3477
fu_device_set_version_raw(FuDevice *self, guint64 version_raw)
3478
0
{
3479
0
  FuDeviceClass *device_class = FU_DEVICE_GET_CLASS(self);
3480
0
  g_return_if_fail(FU_IS_DEVICE(self));
3481
3482
0
  fwupd_device_set_version_raw(FWUPD_DEVICE(self), version_raw);
3483
0
  if (device_class->convert_version != NULL) {
3484
0
    g_autofree gchar *version = device_class->convert_version(self, version_raw);
3485
0
    if (version != NULL)
3486
0
      fu_device_set_version(self, version);
3487
0
  }
3488
0
}
3489
3490
/**
3491
 * fu_device_set_version_lowest_raw:
3492
 * @self: a #FuDevice
3493
 * @version_raw: an integer
3494
 *
3495
 * Sets the raw device version from a integer value and the device version format.
3496
 *
3497
 * Since: 2.0.7
3498
 **/
3499
void
3500
fu_device_set_version_lowest_raw(FuDevice *self, guint64 version_raw)
3501
0
{
3502
0
  FuDeviceClass *device_class = FU_DEVICE_GET_CLASS(self);
3503
0
  g_return_if_fail(FU_IS_DEVICE(self));
3504
3505
0
  fwupd_device_set_version_lowest_raw(FWUPD_DEVICE(self), version_raw);
3506
0
  if (device_class->convert_version != NULL) {
3507
0
    g_autofree gchar *version = device_class->convert_version(self, version_raw);
3508
0
    if (version != NULL)
3509
0
      fu_device_set_version_lowest(self, version);
3510
0
  }
3511
0
}
3512
3513
/* private */
3514
gboolean
3515
fu_device_is_updatable(FuDevice *self)
3516
0
{
3517
0
  g_return_val_if_fail(FU_IS_DEVICE(self), FALSE);
3518
0
  return fu_device_has_flag(self, FWUPD_DEVICE_FLAG_UPDATABLE) ||
3519
0
         fu_device_has_flag(self, FWUPD_DEVICE_FLAG_UPDATABLE_HIDDEN);
3520
0
}
3521
3522
static void
3523
fu_device_inhibit_free(FuDeviceInhibit *inhibit)
3524
0
{
3525
0
  g_free(inhibit->inhibit_id);
3526
0
  g_free(inhibit->reason);
3527
0
  g_free(inhibit);
3528
0
}
3529
3530
static void
3531
fu_device_ensure_inhibits(FuDevice *self)
3532
0
{
3533
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
3534
0
  FwupdDeviceProblem problems = FWUPD_DEVICE_PROBLEM_NONE;
3535
0
  guint nr_inhibits = g_hash_table_size(priv->inhibits);
3536
3537
  /* was okay -> not okay */
3538
0
  if (nr_inhibits > 0) {
3539
0
    g_autofree gchar *reasons_str = NULL;
3540
0
    g_autoptr(GList) values = g_hash_table_get_values(priv->inhibits);
3541
0
    g_autoptr(GPtrArray) reasons = g_ptr_array_new();
3542
3543
    /* updatable -> updatable-hidden -- which is required as devices might have
3544
     * inhibits and *not* be automatically updatable */
3545
0
    if (fu_device_has_flag(self, FWUPD_DEVICE_FLAG_UPDATABLE)) {
3546
0
      fu_device_remove_flag(self, FWUPD_DEVICE_FLAG_UPDATABLE);
3547
0
      fwupd_device_add_flag(FWUPD_DEVICE(self),
3548
0
                FWUPD_DEVICE_FLAG_UPDATABLE_HIDDEN);
3549
0
    }
3550
3551
    /* update the update error */
3552
0
    for (GList *l = values; l != NULL; l = l->next) {
3553
0
      FuDeviceInhibit *inhibit = (FuDeviceInhibit *)l->data;
3554
0
      g_ptr_array_add(reasons, inhibit->reason);
3555
0
      problems |= inhibit->problem;
3556
0
    }
3557
0
    reasons_str = fu_strjoin(", ", reasons);
3558
0
    fu_device_set_update_error(self, reasons_str);
3559
0
  } else {
3560
0
    if (fu_device_has_flag(self, FWUPD_DEVICE_FLAG_UPDATABLE_HIDDEN)) {
3561
0
      fu_device_remove_flag(self, FWUPD_DEVICE_FLAG_UPDATABLE_HIDDEN);
3562
0
      fwupd_device_add_flag(FWUPD_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE);
3563
0
    }
3564
0
    fu_device_set_update_error(self, NULL);
3565
0
  }
3566
3567
  /* sync with baseclass */
3568
0
  fwupd_device_set_problems(FWUPD_DEVICE(self), problems);
3569
0
}
3570
3571
static gchar *
3572
fu_device_problem_to_inhibit_reason(FuDevice *self, guint64 device_problem)
3573
0
{
3574
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
3575
0
  if (device_problem == FWUPD_DEVICE_PROBLEM_UNREACHABLE)
3576
0
    return g_strdup("Device is unreachable, or out of wireless range");
3577
0
  if (device_problem == FWUPD_DEVICE_PROBLEM_UPDATE_PENDING)
3578
0
    return g_strdup("Device is waiting for the update to be applied");
3579
0
  if (device_problem == FWUPD_DEVICE_PROBLEM_REQUIRE_AC_POWER)
3580
0
    return g_strdup("Device requires AC power to be connected");
3581
0
  if (device_problem == FWUPD_DEVICE_PROBLEM_LID_IS_CLOSED)
3582
0
    return g_strdup("Device cannot be used while the lid is closed");
3583
0
  if (device_problem == FWUPD_DEVICE_PROBLEM_IS_EMULATED)
3584
0
    return g_strdup("Device is emulated");
3585
0
  if (device_problem == FWUPD_DEVICE_PROBLEM_UPDATE_IN_PROGRESS)
3586
0
    return g_strdup("An update is in progress");
3587
0
  if (device_problem == FWUPD_DEVICE_PROBLEM_IN_USE)
3588
0
    return g_strdup("Device is in use");
3589
0
  if (device_problem == FWUPD_DEVICE_PROBLEM_DISPLAY_REQUIRED)
3590
0
    return g_strdup("Device requires a display to be plugged in");
3591
0
  if (device_problem == FWUPD_DEVICE_PROBLEM_LOWER_PRIORITY) {
3592
0
    if (fu_device_get_equivalent_id(self) == NULL)
3593
0
      return g_strdup("Device is lower priority than an equivalent device");
3594
0
    return g_strdup_printf("Device is lower priority than equivalent device %s",
3595
0
               fu_device_get_equivalent_id(self));
3596
0
  }
3597
0
  if (device_problem == FWUPD_DEVICE_PROBLEM_MISSING_LICENSE)
3598
0
    return g_strdup("Device does not have the necessary license installed");
3599
0
  if (device_problem == FWUPD_DEVICE_PROBLEM_SYSTEM_POWER_TOO_LOW) {
3600
0
    if (priv->ctx == NULL)
3601
0
      return g_strdup("System power is too low");
3602
0
    return g_strdup_printf("System power is too low (%u%%, requires %u%%)",
3603
0
               fu_context_get_battery_level(priv->ctx),
3604
0
               fu_context_get_battery_threshold(priv->ctx));
3605
0
  }
3606
0
  if (device_problem == FWUPD_DEVICE_PROBLEM_POWER_TOO_LOW) {
3607
0
    if (fu_device_get_battery_level(self) == FWUPD_BATTERY_LEVEL_INVALID ||
3608
0
        fu_device_get_battery_threshold(self) == FWUPD_BATTERY_LEVEL_INVALID) {
3609
0
      return g_strdup_printf("Device battery power is too low");
3610
0
    }
3611
0
    return g_strdup_printf("Device battery power is too low (%u%%, requires %u%%)",
3612
0
               fu_device_get_battery_level(self),
3613
0
               fu_device_get_battery_threshold(self));
3614
0
  }
3615
0
  return NULL;
3616
0
}
3617
3618
static void
3619
fu_device_inhibit_full(FuDevice *self,
3620
           FwupdDeviceProblem problem,
3621
           const gchar *inhibit_id,
3622
           const gchar *reason)
3623
0
{
3624
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
3625
0
  FuDeviceInhibit *inhibit;
3626
3627
0
  g_return_if_fail(FU_IS_DEVICE(self));
3628
3629
  /* lazy create as most devices will not need this */
3630
0
  if (priv->inhibits == NULL) {
3631
0
    priv->inhibits = g_hash_table_new_full(g_str_hash,
3632
0
                   g_str_equal,
3633
0
                   NULL,
3634
0
                   (GDestroyNotify)fu_device_inhibit_free);
3635
0
  }
3636
3637
  /* can fallback */
3638
0
  if (inhibit_id == NULL)
3639
0
    inhibit_id = fwupd_device_problem_to_string(problem);
3640
3641
  /* already exists */
3642
0
  inhibit = g_hash_table_lookup(priv->inhibits, inhibit_id);
3643
0
  if (inhibit != NULL)
3644
0
    return;
3645
3646
  /* create new */
3647
0
  inhibit = g_new0(FuDeviceInhibit, 1);
3648
0
  inhibit->problem = problem;
3649
0
  inhibit->inhibit_id = g_strdup(inhibit_id);
3650
0
  if (reason != NULL) {
3651
0
    inhibit->reason = g_strdup(reason);
3652
0
  } else {
3653
0
    inhibit->reason = fu_device_problem_to_inhibit_reason(self, problem);
3654
0
  }
3655
0
  g_hash_table_insert(priv->inhibits, inhibit->inhibit_id, inhibit);
3656
3657
  /* refresh */
3658
0
  fu_device_ensure_inhibits(self);
3659
3660
  /* propagate to children */
3661
0
  if (fu_device_has_private_flag(self, FU_DEVICE_PRIVATE_FLAG_INHIBIT_CHILDREN)) {
3662
0
    GPtrArray *children = fu_device_get_children(self);
3663
0
    for (guint i = 0; i < children->len; i++) {
3664
0
      FuDevice *child = g_ptr_array_index(children, i);
3665
0
      fu_device_inhibit(child, inhibit_id, reason);
3666
0
    }
3667
0
  }
3668
0
}
3669
3670
/**
3671
 * fu_device_inhibit:
3672
 * @self: a #FuDevice
3673
 * @inhibit_id: an ID used for uninhibiting, e.g. `low-power`
3674
 * @reason: (nullable): a string, e.g. `Cannot update as foo [bar] needs reboot`
3675
 *
3676
 * Prevent the device from being updated, changing it from %FWUPD_DEVICE_FLAG_UPDATABLE
3677
 * to %FWUPD_DEVICE_FLAG_UPDATABLE_HIDDEN if not already inhibited.
3678
 *
3679
 * If the device already has an inhibit with the same @inhibit_id then the request
3680
 * is ignored.
3681
 *
3682
 * Since: 1.6.0
3683
 **/
3684
void
3685
fu_device_inhibit(FuDevice *self, const gchar *inhibit_id, const gchar *reason)
3686
0
{
3687
0
  g_return_if_fail(FU_IS_DEVICE(self));
3688
0
  g_return_if_fail(inhibit_id != NULL);
3689
0
  fu_device_inhibit_full(self, FWUPD_DEVICE_PROBLEM_NONE, inhibit_id, reason);
3690
0
}
3691
3692
/**
3693
 * fu_device_has_inhibit:
3694
 * @self: a #FuDevice
3695
 * @inhibit_id: an ID used for inhibiting, e.g. `low-power`
3696
 *
3697
 * Check if the device already has an inhibit with a specific ID.
3698
 *
3699
 * Returns: %TRUE if added
3700
 *
3701
 * Since: 1.8.0
3702
 **/
3703
gboolean
3704
fu_device_has_inhibit(FuDevice *self, const gchar *inhibit_id)
3705
0
{
3706
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
3707
3708
0
  g_return_val_if_fail(FU_IS_DEVICE(self), FALSE);
3709
0
  g_return_val_if_fail(inhibit_id != NULL, FALSE);
3710
3711
0
  if (priv->inhibits == NULL)
3712
0
    return FALSE;
3713
0
  return g_hash_table_contains(priv->inhibits, inhibit_id);
3714
0
}
3715
3716
/**
3717
 * fu_device_remove_problem:
3718
 * @self: a #FuDevice
3719
 * @problem: a #FwupdDeviceProblem, e.g. %FWUPD_DEVICE_PROBLEM_SYSTEM_POWER_TOO_LOW
3720
 *
3721
 * Allow the device from being updated if there are no other inhibitors,
3722
 * changing it from %FWUPD_DEVICE_FLAG_UPDATABLE_HIDDEN to %FWUPD_DEVICE_FLAG_UPDATABLE.
3723
 *
3724
 * If the device already has no inhibit with the @inhibit_id then the request
3725
 * is ignored.
3726
 *
3727
 * Since: 1.8.1
3728
 **/
3729
void
3730
fu_device_remove_problem(FuDevice *self, FwupdDeviceProblem problem)
3731
0
{
3732
0
  g_return_if_fail(FU_IS_DEVICE(self));
3733
0
  g_return_if_fail(problem != FWUPD_DEVICE_PROBLEM_UNKNOWN);
3734
0
  return fu_device_uninhibit(self, fwupd_device_problem_to_string(problem));
3735
0
}
3736
3737
/**
3738
 * fu_device_has_problem:
3739
 * @self: a #FuDevice
3740
 * @problem: a #FwupdDeviceProblem, e.g. %FWUPD_DEVICE_PROBLEM_SYSTEM_POWER_TOO_LOW
3741
 *
3742
 * Query if a device has a specific problem.
3743
 *
3744
 * Returns: %TRUE if the device has this problem
3745
 *
3746
 * Since: 1.8.11
3747
 **/
3748
gboolean
3749
fu_device_has_problem(FuDevice *self, FwupdDeviceProblem problem)
3750
0
{
3751
0
  g_return_val_if_fail(FU_IS_DEVICE(self), FALSE);
3752
0
  g_return_val_if_fail(problem != FWUPD_DEVICE_PROBLEM_UNKNOWN, FALSE);
3753
0
  return fu_device_has_inhibit(self, fwupd_device_problem_to_string(problem));
3754
0
}
3755
3756
/**
3757
 * fu_device_add_problem:
3758
 * @self: a #FuDevice
3759
 * @problem: a #FwupdDeviceProblem, e.g. %FWUPD_DEVICE_PROBLEM_SYSTEM_POWER_TOO_LOW
3760
 *
3761
 * Prevent the device from being updated, changing it from %FWUPD_DEVICE_FLAG_UPDATABLE
3762
 * to %FWUPD_DEVICE_FLAG_UPDATABLE_HIDDEN if not already inhibited.
3763
 *
3764
 * If the device already has an inhibit with the same @problem then the request
3765
 * is ignored.
3766
 *
3767
 * Since: 1.8.1
3768
 **/
3769
void
3770
fu_device_add_problem(FuDevice *self, FwupdDeviceProblem problem)
3771
0
{
3772
0
  g_return_if_fail(FU_IS_DEVICE(self));
3773
0
  g_return_if_fail(problem != FWUPD_DEVICE_PROBLEM_UNKNOWN);
3774
0
  fu_device_inhibit_full(self, problem, NULL, NULL);
3775
0
}
3776
3777
/**
3778
 * fu_device_uninhibit:
3779
 * @self: a #FuDevice
3780
 * @inhibit_id: an ID used for uninhibiting, e.g. `low-power`
3781
 *
3782
 * Allow the device from being updated if there are no other inhibitors,
3783
 * changing it from %FWUPD_DEVICE_FLAG_UPDATABLE_HIDDEN to %FWUPD_DEVICE_FLAG_UPDATABLE.
3784
 *
3785
 * If the device already has no inhibit with the @inhibit_id then the request
3786
 * is ignored.
3787
 *
3788
 * Since: 1.6.0
3789
 **/
3790
void
3791
fu_device_uninhibit(FuDevice *self, const gchar *inhibit_id)
3792
0
{
3793
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
3794
3795
0
  g_return_if_fail(FU_IS_DEVICE(self));
3796
0
  g_return_if_fail(inhibit_id != NULL);
3797
3798
0
  if (priv->inhibits == NULL)
3799
0
    return;
3800
0
  if (g_hash_table_remove(priv->inhibits, inhibit_id))
3801
0
    fu_device_ensure_inhibits(self);
3802
3803
  /* propagate to children */
3804
0
  if (fu_device_has_private_flag(self, FU_DEVICE_PRIVATE_FLAG_INHIBIT_CHILDREN)) {
3805
0
    GPtrArray *children = fu_device_get_children(self);
3806
0
    for (guint i = 0; i < children->len; i++) {
3807
0
      FuDevice *child = g_ptr_array_index(children, i);
3808
0
      fu_device_uninhibit(child, inhibit_id);
3809
0
    }
3810
0
  }
3811
0
}
3812
3813
/**
3814
 * fu_device_ensure_id:
3815
 * @self: a #FuDevice
3816
 * @error: (nullable): optional return location for an error
3817
 *
3818
 * If not already set, generates a device ID with the optional physical and
3819
 * logical IDs.
3820
 *
3821
 * Returns: %TRUE on success
3822
 *
3823
 * Since: 1.1.2
3824
 **/
3825
gboolean
3826
fu_device_ensure_id(FuDevice *self, GError **error)
3827
0
{
3828
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
3829
0
  g_autofree gchar *device_id = NULL;
3830
3831
0
  g_return_val_if_fail(FU_IS_DEVICE(self), FALSE);
3832
0
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
3833
3834
  /* already set */
3835
0
  if (priv->device_id_valid)
3836
0
    return TRUE;
3837
3838
  /* nothing we can do! */
3839
0
  if (priv->physical_id == NULL) {
3840
0
    g_autofree gchar *tmp = fu_device_to_string(self);
3841
0
    g_set_error(error,
3842
0
          FWUPD_ERROR,
3843
0
          FWUPD_ERROR_NOT_SUPPORTED,
3844
0
          "cannot ensure ID: %s",
3845
0
          tmp);
3846
0
    return FALSE;
3847
0
  }
3848
3849
  /* logical may be NULL */
3850
0
  device_id =
3851
0
      g_strjoin(":", fu_device_get_physical_id(self), fu_device_get_logical_id(self), NULL);
3852
0
  fu_device_set_id(self, device_id);
3853
0
  return TRUE;
3854
0
}
3855
3856
/**
3857
 * fu_device_get_logical_id:
3858
 * @self: a #FuDevice
3859
 *
3860
 * Gets the logical ID set for the device, which disambiguates devices with the
3861
 * same physical ID.
3862
 *
3863
 * Returns: a string value, or %NULL if never set.
3864
 *
3865
 * Since: 1.1.2
3866
 **/
3867
const gchar *
3868
fu_device_get_logical_id(FuDevice *self)
3869
0
{
3870
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
3871
0
  g_return_val_if_fail(FU_IS_DEVICE(self), NULL);
3872
0
  return priv->logical_id;
3873
0
}
3874
3875
/**
3876
 * fu_device_set_logical_id:
3877
 * @self: a #FuDevice
3878
 * @logical_id: a string, e.g. `dev2`
3879
 *
3880
 * Sets the logical ID on the device. This is designed to disambiguate devices
3881
 * with the same physical ID.
3882
 *
3883
 * Since: 1.1.2
3884
 **/
3885
void
3886
fu_device_set_logical_id(FuDevice *self, const gchar *logical_id)
3887
0
{
3888
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
3889
0
  g_return_if_fail(FU_IS_DEVICE(self));
3890
3891
  /* not changed */
3892
0
  if (g_strcmp0(priv->logical_id, logical_id) == 0)
3893
0
    return;
3894
3895
  /* not allowed after ->probe() and ->setup() have completed */
3896
0
  if (priv->done_setup) {
3897
0
    g_warning("cannot change %s logical ID from %s to %s as "
3898
0
        "FuDevice->setup() has already completed",
3899
0
        fu_device_get_id(self),
3900
0
        priv->logical_id,
3901
0
        logical_id);
3902
0
    return;
3903
0
  }
3904
3905
0
  g_free(priv->logical_id);
3906
0
  priv->logical_id = g_strdup(logical_id);
3907
0
  priv->device_id_valid = FALSE;
3908
0
  g_object_notify(G_OBJECT(self), "logical-id");
3909
0
}
3910
3911
/**
3912
 * fu_device_get_backend_id:
3913
 * @self: a #FuDevice
3914
 *
3915
 * Gets the ID set for the device as recognized by the backend. This is typically
3916
 * a Linux sysfs path or USB platform ID. If unset, it also falls back to the
3917
 * physical ID as this may be the same value.
3918
 *
3919
 * Returns: a string value, or %NULL if never set.
3920
 *
3921
 * Since: 1.5.8
3922
 **/
3923
const gchar *
3924
fu_device_get_backend_id(FuDevice *self)
3925
0
{
3926
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
3927
0
  g_return_val_if_fail(FU_IS_DEVICE(self), NULL);
3928
0
  if (priv->backend_id != NULL)
3929
0
    return priv->backend_id;
3930
0
  return priv->physical_id;
3931
0
}
3932
3933
/**
3934
 * fu_device_set_backend_id:
3935
 * @self: a #FuDevice
3936
 * @backend_id: a string, e.g. `dev2`
3937
 *
3938
 * Sets the backend ID on the device. This is designed to disambiguate devices
3939
 * with the same physical ID. This is typically a Linux sysfs path or USB
3940
 * platform ID.
3941
 *
3942
 * Since: 1.5.8
3943
 **/
3944
void
3945
fu_device_set_backend_id(FuDevice *self, const gchar *backend_id)
3946
0
{
3947
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
3948
0
  g_return_if_fail(FU_IS_DEVICE(self));
3949
3950
  /* not changed */
3951
0
  if (g_strcmp0(priv->backend_id, backend_id) == 0)
3952
0
    return;
3953
3954
0
  g_free(priv->backend_id);
3955
0
  priv->backend_id = g_strdup(backend_id);
3956
0
  priv->device_id_valid = FALSE;
3957
0
  g_object_notify(G_OBJECT(self), "backend-id");
3958
0
}
3959
3960
/**
3961
 * fu_device_get_backend:
3962
 * @self: a #FuDevice
3963
 *
3964
 * Gets the backend, if set with fu_device_set_backend().
3965
 *
3966
 * Returns: (transfer none): a #FuBackend or %NULL if not sset
3967
 *
3968
 * Since: 2.0.0
3969
 **/
3970
FuBackend *
3971
fu_device_get_backend(FuDevice *self)
3972
0
{
3973
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
3974
0
  g_return_val_if_fail(FU_IS_DEVICE(self), NULL);
3975
0
  return priv->backend;
3976
0
}
3977
3978
/**
3979
 * fu_device_set_backend:
3980
 * @self: a #FuDevice
3981
 * @backend: a #FuBackend
3982
 *
3983
 * Sets the backend that created this device.
3984
 *
3985
 * Since: 2.0.0
3986
 **/
3987
void
3988
fu_device_set_backend(FuDevice *self, FuBackend *backend)
3989
0
{
3990
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
3991
3992
0
  g_return_if_fail(FU_IS_DEVICE(self));
3993
0
  g_return_if_fail(backend == NULL || FU_IS_BACKEND(backend));
3994
3995
  /* same */
3996
0
  if (priv->backend == backend)
3997
0
    return;
3998
3999
  /* not already set */
4000
0
  if (priv->ctx == NULL)
4001
0
    fu_device_set_context(self, fu_backend_get_context(backend));
4002
4003
  /* there is no ref on backend to prevent a loop */
4004
0
  if (priv->backend != NULL)
4005
0
    g_object_remove_weak_pointer(G_OBJECT(priv->backend), (gpointer *)&priv->backend);
4006
0
  if (backend != NULL)
4007
0
    g_object_add_weak_pointer(G_OBJECT(backend), (gpointer *)&priv->backend);
4008
0
  priv->backend = backend; /* no ref */
4009
4010
  /* in case anything cares */
4011
0
  g_object_notify(G_OBJECT(self), "backend");
4012
0
}
4013
4014
/**
4015
 * fu_device_get_backend_parent_with_subsystem:
4016
 * @self: a #FuDevice
4017
 * @subsystem: (nullable): an optional device subsystem, e.g. "usb:usb_device"
4018
 * @error: (nullable): optional return location for an error
4019
 *
4020
 * Creates a device parent (of the correct type) using the current backend for a given device kind.
4021
 *
4022
 * NOTE: The backend must implement `FuBackendClass->get_device_parent` for this method to work --
4023
 * for cases where the plugin has created both parent and child, and used `fu_device_add_child()`,
4024
 * using `fu_device_get_parent()` is probably more appropriate.
4025
 *
4026
 * Returns: (transfer full): a #FuDevice or %NULL if not found or unimplemented
4027
 *
4028
 * Since: 2.0.0
4029
 **/
4030
FuDevice *
4031
fu_device_get_backend_parent_with_subsystem(FuDevice *self, const gchar *subsystem, GError **error)
4032
0
{
4033
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
4034
0
  FuDeviceEvent *event = NULL;
4035
0
  g_autofree gchar *event_id = NULL;
4036
0
  g_autoptr(FuDevice) parent = NULL;
4037
0
  g_autoptr(GError) error_local = NULL;
4038
4039
0
  g_return_val_if_fail(FU_IS_DEVICE(self), NULL);
4040
0
  g_return_val_if_fail(error == NULL || *error == NULL, NULL);
4041
4042
  /* sanity check */
4043
0
  if (priv->backend == NULL) {
4044
0
    g_set_error_literal(error,
4045
0
            FWUPD_ERROR,
4046
0
            FWUPD_ERROR_NOT_SUPPORTED,
4047
0
            "no backend set for device");
4048
0
    return NULL;
4049
0
  }
4050
4051
  /* need event ID */
4052
0
  if (fu_device_has_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_EMULATED) ||
4053
0
      fu_context_has_flag(fu_device_get_context(FU_DEVICE(self)),
4054
0
        FU_CONTEXT_FLAG_SAVE_EVENTS)) {
4055
0
    event_id = g_strdup_printf("GetBackendParent:Subsystem=%s", subsystem);
4056
0
  }
4057
4058
  /* emulated */
4059
0
  if (fu_device_has_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_EMULATED)) {
4060
0
    GType gtype;
4061
0
    const gchar *gtype_str;
4062
0
    const gchar *id;
4063
4064
    /* we might have to propagate this to preserve compat with older emulation files */
4065
0
    if (fu_device_check_fwupd_version(self, "2.0.8")) {
4066
0
      event = fu_device_load_event(FU_DEVICE(self), event_id, error);
4067
0
      if (event == NULL)
4068
0
        return NULL;
4069
0
      if (!fu_device_event_check_error(event, error))
4070
0
        return NULL;
4071
0
    } else {
4072
0
      event = fu_device_load_event(FU_DEVICE(self), event_id, NULL);
4073
0
      if (event == NULL) {
4074
0
        g_debug("falling back for emulation recorded by fwupd %s",
4075
0
          priv->fwupd_version);
4076
0
        parent = fu_backend_get_device_parent(priv->backend,
4077
0
                      self,
4078
0
                      subsystem,
4079
0
                      error);
4080
0
        if (parent != self)
4081
0
          fu_device_set_target(parent, self);
4082
0
        return g_steal_pointer(&parent);
4083
0
      }
4084
0
    }
4085
4086
    /* missing GType is 'no parent found' */
4087
0
    gtype_str = fu_device_event_get_str(event, "GType", NULL);
4088
0
    if (gtype_str == NULL) {
4089
0
      g_set_error(error,
4090
0
            FWUPD_ERROR,
4091
0
            FWUPD_ERROR_NOT_FOUND,
4092
0
            "no parent with subsystem %s",
4093
0
            subsystem);
4094
0
      return NULL;
4095
0
    }
4096
0
    gtype = g_type_from_name(gtype_str);
4097
0
    if (gtype == G_TYPE_INVALID) {
4098
0
      g_set_error(error,
4099
0
            FWUPD_ERROR,
4100
0
            FWUPD_ERROR_NOT_FOUND,
4101
0
            "no GType %s",
4102
0
            gtype_str);
4103
0
      return NULL;
4104
0
    }
4105
0
    parent =
4106
0
        g_object_new(gtype, "context", fu_device_get_context(FU_DEVICE(self)), NULL);
4107
0
    fu_device_add_flag(parent, FWUPD_DEVICE_FLAG_EMULATED);
4108
0
    id = fu_device_event_get_str(event, "DeviceId", NULL);
4109
0
    if (id != NULL)
4110
0
      fu_device_set_id(parent, id);
4111
0
    id = fu_device_event_get_str(event, "BackendId", NULL);
4112
0
    if (id != NULL)
4113
0
      fu_device_set_backend_id(parent, id);
4114
0
    id = fu_device_event_get_str(event, "PhysicalId", NULL);
4115
0
    if (id != NULL)
4116
0
      fu_device_set_physical_id(parent, id);
4117
0
    if (parent != self)
4118
0
      fu_device_set_target(parent, self);
4119
0
    return g_steal_pointer(&parent);
4120
0
  }
4121
4122
  /* save */
4123
0
  if (event_id != NULL)
4124
0
    event = fu_device_save_event(FU_DEVICE(self), event_id);
4125
4126
  /* call into the backend */
4127
0
  parent = fu_backend_get_device_parent(priv->backend, self, subsystem, &error_local);
4128
0
  if (parent == NULL) {
4129
0
    if (event != NULL)
4130
0
      fu_device_event_set_error(event, error_local);
4131
0
    g_propagate_error(error, g_steal_pointer(&error_local));
4132
0
    return NULL;
4133
0
  }
4134
0
  if (!fu_device_probe(parent, &error_local)) {
4135
0
    if (event != NULL)
4136
0
      fu_device_event_set_error(event, error_local);
4137
0
    g_propagate_error(error, g_steal_pointer(&error_local));
4138
0
    return NULL;
4139
0
  }
4140
4141
  /* save response */
4142
0
  if (event != NULL) {
4143
0
    fu_device_event_set_str(event, "GType", G_OBJECT_TYPE_NAME(parent));
4144
0
    if (fu_device_get_id(self) != NULL)
4145
0
      fu_device_event_set_str(event, "DeviceId", fu_device_get_id(parent));
4146
0
    if (fu_device_get_backend_id(parent) != NULL) {
4147
0
      fu_device_event_set_str(event,
4148
0
            "BackendId",
4149
0
            fu_device_get_backend_id(parent));
4150
0
    }
4151
0
    if (fu_device_get_physical_id(parent) != NULL) {
4152
0
      fu_device_event_set_str(event,
4153
0
            "PhysicalId",
4154
0
            fu_device_get_physical_id(parent));
4155
0
    }
4156
0
  }
4157
4158
0
  if (parent != self)
4159
0
    fu_device_set_target(parent, self);
4160
0
  return g_steal_pointer(&parent);
4161
0
}
4162
4163
/**
4164
 * fu_device_get_backend_parent:
4165
 * @self: a #FuDevice
4166
 * @error: (nullable): optional return location for an error
4167
 *
4168
 * Creates a device parent (of the correct type) using the current backend.
4169
 *
4170
 * NOTE: The backend must implement `FuBackendClass->get_device_parent` for this method to work --
4171
 * for cases where the plugin has created both parent and child, and used `fu_device_add_child()`,
4172
 * using `fu_device_get_parent()` is probably more appropriate.
4173
 *
4174
 * Returns: (transfer full): a #FuDevice or %NULL if not found or unimplemented
4175
 *
4176
 * Since: 2.0.0
4177
 **/
4178
FuDevice *
4179
fu_device_get_backend_parent(FuDevice *self, GError **error)
4180
0
{
4181
0
  g_return_val_if_fail(FU_IS_DEVICE(self), NULL);
4182
0
  g_return_val_if_fail(error == NULL || *error == NULL, NULL);
4183
0
  return fu_device_get_backend_parent_with_subsystem(self, NULL, error);
4184
0
}
4185
4186
/**
4187
 * fu_device_get_update_request_id:
4188
 * @self: a #FuDevice
4189
 *
4190
 * Gets the update request ID as specified from `LVFS::UpdateRequestId`.
4191
 *
4192
 * Returns: a string value, or %NULL if never set.
4193
 *
4194
 * Since: 1.8.6
4195
 **/
4196
const gchar *
4197
fu_device_get_update_request_id(FuDevice *self)
4198
0
{
4199
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
4200
0
  g_return_val_if_fail(FU_IS_DEVICE(self), NULL);
4201
0
  return priv->update_request_id;
4202
0
}
4203
4204
/**
4205
 * fu_device_set_update_request_id:
4206
 * @self: a #FuDevice
4207
 * @update_request_id: a string, e.g. `org.freedesktop.fwupd.request.do-not-power-off`
4208
 *
4209
 * Sets the update request ID as specified in `LVFS::UpdateRequestId`.
4210
 *
4211
 * Since: 1.8.6
4212
 **/
4213
void
4214
fu_device_set_update_request_id(FuDevice *self, const gchar *update_request_id)
4215
0
{
4216
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
4217
0
  g_return_if_fail(FU_IS_DEVICE(self));
4218
4219
  /* not changed */
4220
0
  if (g_strcmp0(priv->update_request_id, update_request_id) == 0)
4221
0
    return;
4222
4223
0
  g_free(priv->update_request_id);
4224
0
  priv->update_request_id = g_strdup(update_request_id);
4225
0
}
4226
4227
/**
4228
 * fu_device_get_update_message:
4229
 * @self: a #FuDevice
4230
 *
4231
 * Gets the update message string.
4232
 *
4233
 * Returns: the update message string, or %NULL if unset
4234
 *
4235
 * Since: 2.0.0
4236
 **/
4237
const gchar *
4238
fu_device_get_update_message(FuDevice *self)
4239
0
{
4240
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
4241
0
  g_return_val_if_fail(FU_IS_DEVICE(self), NULL);
4242
0
  return priv->update_message;
4243
0
}
4244
4245
/**
4246
 * fu_device_set_update_message:
4247
 * @self: a #FuDevice
4248
 * @update_message: (nullable): the update message string
4249
 *
4250
 * Sets the update message string.
4251
 *
4252
 * Since: 2.0.0
4253
 **/
4254
void
4255
fu_device_set_update_message(FuDevice *self, const gchar *update_message)
4256
0
{
4257
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
4258
0
  g_return_if_fail(FU_IS_DEVICE(self));
4259
4260
  /* not changed */
4261
0
  if (g_strcmp0(priv->update_message, update_message) == 0)
4262
0
    return;
4263
4264
0
  g_free(priv->update_message);
4265
0
  priv->update_message = g_strdup(update_message);
4266
0
  g_object_notify(G_OBJECT(self), "update-message");
4267
0
}
4268
4269
/**
4270
 * fu_device_get_update_image:
4271
 * @self: a #FuDevice
4272
 *
4273
 * Gets the update image URL.
4274
 *
4275
 * Returns: the update image URL, or %NULL if unset
4276
 *
4277
 * Since: 2.0.0
4278
 **/
4279
const gchar *
4280
fu_device_get_update_image(FuDevice *self)
4281
0
{
4282
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
4283
0
  g_return_val_if_fail(FU_IS_DEVICE(self), NULL);
4284
0
  return priv->update_image;
4285
0
}
4286
4287
/**
4288
 * fu_device_set_update_image:
4289
 * @self: a #FuDevice
4290
 * @update_image: (nullable): the update image URL
4291
 *
4292
 * Sets the update image URL.
4293
 *
4294
 * Since: 2.0.0
4295
 **/
4296
void
4297
fu_device_set_update_image(FuDevice *self, const gchar *update_image)
4298
0
{
4299
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
4300
0
  g_return_if_fail(FU_IS_DEVICE(self));
4301
4302
  /* not changed */
4303
0
  if (g_strcmp0(priv->update_image, update_image) == 0)
4304
0
    return;
4305
4306
0
  g_free(priv->update_image);
4307
0
  priv->update_image = g_strdup(update_image);
4308
0
  g_object_notify(G_OBJECT(self), "update-image");
4309
0
}
4310
4311
/**
4312
 * fu_device_check_fwupd_version:
4313
 * @self: a #FuDevice
4314
 * @fwupd_version: (not nullable): the version, e.g. `2.0.8`
4315
 *
4316
 * Checks the fwupd version that created the emulation.
4317
 *
4318
 * Returns: %TRUE if @fwupd_version version is >= to the emulation version
4319
 *
4320
 * Since: 2.0.13
4321
 **/
4322
gboolean
4323
fu_device_check_fwupd_version(FuDevice *self, const gchar *fwupd_version)
4324
0
{
4325
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
4326
0
  g_return_val_if_fail(FU_IS_DEVICE(self), FALSE);
4327
0
  g_return_val_if_fail(fwupd_version != NULL, FALSE);
4328
0
  if (priv->fwupd_version == NULL)
4329
0
    return FALSE;
4330
0
  return fu_version_compare(priv->fwupd_version,
4331
0
          fwupd_version,
4332
0
          FWUPD_VERSION_FORMAT_TRIPLET) >= 0;
4333
0
}
4334
4335
/**
4336
 * fu_device_set_fwupd_version:
4337
 * @self: a #FuDevice
4338
 * @fwupd_version: (nullable): the version, e.g. `2.0.8`
4339
 *
4340
 * Sets the fwupd version that created the emulation.
4341
 *
4342
 * NOTE: This can only be set on devices with the %FWUPD_DEVICE_FLAG_EMULATED flag set.
4343
 *
4344
 * Since: 2.0.8
4345
 **/
4346
void
4347
fu_device_set_fwupd_version(FuDevice *self, const gchar *fwupd_version)
4348
0
{
4349
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
4350
0
  g_return_if_fail(FU_IS_DEVICE(self));
4351
0
  g_return_if_fail(fu_device_has_flag(self, FWUPD_DEVICE_FLAG_EMULATED));
4352
4353
  /* not changed */
4354
0
  if (g_strcmp0(priv->fwupd_version, fwupd_version) == 0)
4355
0
    return;
4356
4357
0
  g_free(priv->fwupd_version);
4358
0
  priv->fwupd_version = g_strdup(fwupd_version);
4359
0
}
4360
4361
/**
4362
 * fu_device_get_proxy_guid:
4363
 * @self: a #FuDevice
4364
 *
4365
 * Gets the proxy GUID device, which is set to let the engine match up the
4366
 * proxy between plugins.
4367
 *
4368
 * Returns: a string value, or %NULL if never set.
4369
 *
4370
 * Since: 1.4.1
4371
 **/
4372
const gchar *
4373
fu_device_get_proxy_guid(FuDevice *self)
4374
0
{
4375
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
4376
0
  g_return_val_if_fail(FU_IS_DEVICE(self), NULL);
4377
0
  return priv->proxy_guid;
4378
0
}
4379
4380
/**
4381
 * fu_device_set_proxy_guid:
4382
 * @self: a #FuDevice
4383
 * @proxy_guid: a string, e.g. `USB\VID_413C&PID_B06E&hub`
4384
 *
4385
 * Sets the GUID of the proxy device. The proxy device may update @self.
4386
 *
4387
 * Since: 1.4.1
4388
 **/
4389
void
4390
fu_device_set_proxy_guid(FuDevice *self, const gchar *proxy_guid)
4391
0
{
4392
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
4393
0
  g_return_if_fail(FU_IS_DEVICE(self));
4394
4395
  /* not changed */
4396
0
  if (g_strcmp0(priv->proxy_guid, proxy_guid) == 0)
4397
0
    return;
4398
4399
0
  g_free(priv->proxy_guid);
4400
0
  priv->proxy_guid = g_strdup(proxy_guid);
4401
0
}
4402
4403
/**
4404
 * fu_device_set_physical_id:
4405
 * @self: a #FuDevice
4406
 * @physical_id: a string that identifies the physical device connection
4407
 *
4408
 * Sets the physical ID on the device which represents the electrical connection
4409
 * of the device to the system. Multiple #FuDevices can share a physical ID.
4410
 *
4411
 * The physical ID is used to remove logical devices when a physical device has
4412
 * been removed from the system.
4413
 *
4414
 * A sysfs or devpath is not a physical ID, but could be something like
4415
 * `PCI_SLOT_NAME=0000:3e:00.0`.
4416
 *
4417
 * Since: 1.1.2
4418
 **/
4419
void
4420
fu_device_set_physical_id(FuDevice *self, const gchar *physical_id)
4421
0
{
4422
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
4423
0
  g_return_if_fail(FU_IS_DEVICE(self));
4424
0
  g_return_if_fail(physical_id != NULL);
4425
4426
  /* not changed */
4427
0
  if (g_strcmp0(priv->physical_id, physical_id) == 0)
4428
0
    return;
4429
4430
  /* not allowed after ->probe() and ->setup() have completed */
4431
0
  if (priv->done_setup) {
4432
0
    g_warning("cannot change %s physical ID from %s to %s as "
4433
0
        "FuDevice->setup() has already completed",
4434
0
        fu_device_get_id(self),
4435
0
        priv->physical_id,
4436
0
        physical_id);
4437
0
    return;
4438
0
  }
4439
4440
0
  g_free(priv->physical_id);
4441
0
  priv->physical_id = g_strdup(physical_id);
4442
0
  priv->device_id_valid = FALSE;
4443
0
  g_object_notify(G_OBJECT(self), "physical-id");
4444
0
}
4445
4446
/**
4447
 * fu_device_get_physical_id:
4448
 * @self: a #FuDevice
4449
 *
4450
 * Gets the physical ID set for the device, which represents the electrical
4451
 * connection used to compare devices.
4452
 *
4453
 * Multiple #FuDevices can share a single physical ID.
4454
 *
4455
 * Returns: a string value, or %NULL if never set.
4456
 *
4457
 * Since: 1.1.2
4458
 **/
4459
const gchar *
4460
fu_device_get_physical_id(FuDevice *self)
4461
0
{
4462
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
4463
0
  g_return_val_if_fail(FU_IS_DEVICE(self), NULL);
4464
0
  return priv->physical_id;
4465
0
}
4466
4467
/**
4468
 * fu_device_remove_flag:
4469
 * @self: a #FuDevice
4470
 * @flag: a device flag
4471
 *
4472
 * Removes a device flag from the device.
4473
 *
4474
 * Since: 1.6.0
4475
 **/
4476
void
4477
fu_device_remove_flag(FuDevice *self, FwupdDeviceFlags flag)
4478
0
{
4479
  /* proxy */
4480
0
  fwupd_device_remove_flag(FWUPD_DEVICE(self), flag);
4481
4482
  /* allow it to be updatable again */
4483
0
  if (flag & FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION)
4484
0
    fu_device_uninhibit(self, "needs-activation");
4485
0
  if (flag & FWUPD_DEVICE_FLAG_UNREACHABLE)
4486
0
    fu_device_uninhibit(self, "unreachable");
4487
0
}
4488
4489
/**
4490
 * fu_device_add_flag:
4491
 * @self: a #FuDevice
4492
 * @flag: a device flag
4493
 *
4494
 * Adds a device flag to the device.
4495
 *
4496
 * Since: 0.1.0
4497
 **/
4498
void
4499
fu_device_add_flag(FuDevice *self, FwupdDeviceFlags flag)
4500
0
{
4501
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
4502
4503
  /* none is not used as an "exported" flag */
4504
0
  if (flag == FWUPD_DEVICE_FLAG_NONE)
4505
0
    return;
4506
4507
  /* emulated device reinstalling do not need a replug or shutdown */
4508
0
  if (flag == FWUPD_DEVICE_FLAG_EMULATED) {
4509
0
    if (fu_device_has_flag(self, FWUPD_DEVICE_FLAG_NEEDS_REBOOT)) {
4510
0
      g_debug("removing needs-reboot for emulated device");
4511
0
      fu_device_remove_flag(self, FWUPD_DEVICE_FLAG_NEEDS_REBOOT);
4512
0
    }
4513
0
    if (fu_device_has_flag(self, FWUPD_DEVICE_FLAG_NEEDS_SHUTDOWN)) {
4514
0
      g_debug("removing needs-shutdown for emulated device");
4515
0
      fu_device_remove_flag(self, FWUPD_DEVICE_FLAG_NEEDS_SHUTDOWN);
4516
0
    }
4517
0
  }
4518
4519
  /* we only inhibit when the flags contains UPDATABLE, and that might be discovered by
4520
   * probing the hardware *after* the battery level has been set */
4521
0
  if (flag == FWUPD_DEVICE_FLAG_UPDATABLE && priv->inhibits != NULL)
4522
0
    fu_device_ensure_inhibits(self);
4523
4524
  /* an emulated device cannot be used to record emulation events,
4525
   * and an emulated device cannot be tagged for emulation */
4526
0
  if (flag == FWUPD_DEVICE_FLAG_EMULATED)
4527
0
    fu_device_remove_flag(self, FWUPD_DEVICE_FLAG_CAN_EMULATION_TAG);
4528
0
  if (flag == FWUPD_DEVICE_FLAG_CAN_EMULATION_TAG &&
4529
0
      fu_device_has_flag(self, FWUPD_DEVICE_FLAG_EMULATED))
4530
0
    return;
4531
4532
  /* being both a bootloader and requiring a bootloader is invalid */
4533
0
  if (flag & FWUPD_DEVICE_FLAG_NEEDS_BOOTLOADER)
4534
0
    fu_device_remove_flag(self, FWUPD_DEVICE_FLAG_IS_BOOTLOADER);
4535
0
  if (flag & FWUPD_DEVICE_FLAG_IS_BOOTLOADER)
4536
0
    fu_device_remove_flag(self, FWUPD_DEVICE_FLAG_NEEDS_BOOTLOADER);
4537
4538
  /* being both a signed and unsigned is invalid */
4539
0
  if (flag & FWUPD_DEVICE_FLAG_SIGNED_PAYLOAD)
4540
0
    fu_device_remove_flag(self, FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD);
4541
0
  if (flag & FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD)
4542
0
    fu_device_remove_flag(self, FWUPD_DEVICE_FLAG_SIGNED_PAYLOAD);
4543
4544
  /* one implies the other */
4545
0
  if (flag & FWUPD_DEVICE_FLAG_CAN_VERIFY_IMAGE)
4546
0
    flag |= FWUPD_DEVICE_FLAG_CAN_VERIFY;
4547
0
  if (flag & FWUPD_DEVICE_FLAG_INSTALL_ALL_RELEASES)
4548
0
    flag |= FWUPD_DEVICE_FLAG_VERSION_CHECK_REQUIRED;
4549
0
  fwupd_device_add_flag(FWUPD_DEVICE(self), flag);
4550
4551
  /* activatable devices shouldn't be allowed to update again until activated */
4552
  /* don't let devices be updated until activated */
4553
0
  if (flag & FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION)
4554
0
    fu_device_inhibit(self, "needs-activation", "Pending activation");
4555
4556
  /* do not let devices be updated until back in range */
4557
0
  if (flag & FWUPD_DEVICE_FLAG_UNREACHABLE)
4558
0
    fu_device_add_problem(self, FWUPD_DEVICE_PROBLEM_UNREACHABLE);
4559
4560
  /* fixup and maybe show a warning if the remove delay was forgotten */
4561
0
  if ((flag & FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG) && priv->remove_delay == 0) {
4562
0
    priv->remove_delay = FU_DEVICE_REMOVE_DELAY_RE_ENUMERATE;
4563
0
#ifndef SUPPORTED_BUILD
4564
0
    g_critical("FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG added but remove delay is unset! -- "
4565
0
         "add something like fu_device_set_remove_delay(FU_DEVICE(self), "
4566
0
         "FU_DEVICE_REMOVE_DELAY_RE_ENUMERATE) to the %s _init()",
4567
0
         G_OBJECT_TYPE_NAME(self));
4568
0
#endif
4569
0
  }
4570
0
}
4571
4572
/**
4573
 * fu_device_register_private_flag:
4574
 * @self: a #FuDevice
4575
 * @flag: a string
4576
 *
4577
 * Registers a private device flag so that it can be set from quirk files and printed
4578
 * correctly in debug output.
4579
 *
4580
 * Since: 2.0.0
4581
 **/
4582
void
4583
fu_device_register_private_flag(FuDevice *self, const gchar *flag)
4584
0
{
4585
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
4586
0
  GQuark flag_quark;
4587
4588
0
  g_return_if_fail(FU_IS_DEVICE(self));
4589
0
  g_return_if_fail(flag != NULL);
4590
4591
0
#ifndef SUPPORTED_BUILD
4592
  /* ensure not already the name of an internal or exported flag */
4593
0
  if (fwupd_device_flag_from_string(flag) != FWUPD_DEVICE_FLAG_UNKNOWN) {
4594
0
    g_critical("%s private flag %s already exists as an exported flag",
4595
0
         G_OBJECT_TYPE_NAME(self),
4596
0
         flag);
4597
0
    return;
4598
0
  }
4599
0
#endif
4600
  /* ensure */
4601
0
  fu_device_register_private_flags(self);
4602
4603
  /* sanity check */
4604
0
  flag_quark = fu_device_find_private_flag_quark(self, flag);
4605
0
  if (G_UNLIKELY(flag_quark != 0)) {
4606
0
    g_critical("already registered private %s flag %s", G_OBJECT_TYPE_NAME(self), flag);
4607
0
    return;
4608
0
  }
4609
4610
  /* add new */
4611
0
  flag_quark = g_quark_from_static_string(flag);
4612
0
  g_array_append_val(priv->private_flags_registered, flag_quark);
4613
0
}
4614
4615
static void
4616
fu_device_set_custom_flag(FuDevice *self, const gchar *hint)
4617
0
{
4618
0
  FwupdDeviceFlags flag;
4619
0
  GQuark flag_quark;
4620
4621
0
  g_return_if_fail(hint != NULL);
4622
4623
  /* is this a negated device flag */
4624
0
  if (g_str_has_prefix(hint, "~")) {
4625
0
    flag = fwupd_device_flag_from_string(hint + 1);
4626
0
    if (flag != FWUPD_DEVICE_FLAG_UNKNOWN) {
4627
0
      fu_device_remove_flag(self, flag);
4628
0
      return;
4629
0
    }
4630
0
    flag_quark = fu_device_find_private_flag_quark(self, hint + 1);
4631
0
    if (flag_quark != 0) {
4632
0
      fu_device_remove_private_flag_quark(self, flag_quark);
4633
0
      return;
4634
0
    }
4635
0
    return;
4636
0
  }
4637
4638
  /* is this a known device flag */
4639
0
  flag = fwupd_device_flag_from_string(hint);
4640
0
  if (flag != FWUPD_DEVICE_FLAG_UNKNOWN) {
4641
0
    fu_device_add_flag(self, flag);
4642
0
    return;
4643
0
  }
4644
0
  flag_quark = fu_device_find_private_flag_quark(self, hint);
4645
0
  if (flag_quark != 0) {
4646
0
    fu_device_add_private_flag_quark(self, flag_quark);
4647
0
    return;
4648
0
  }
4649
0
}
4650
4651
/**
4652
 * fu_device_set_custom_flags:
4653
 * @self: a #FuDevice
4654
 * @custom_flags: a string
4655
 *
4656
 * Sets the custom flags from the quirk system that can be used to
4657
 * affect device matching. The actual string format is defined by the plugin.
4658
 *
4659
 * Since: 1.1.0
4660
 **/
4661
void
4662
fu_device_set_custom_flags(FuDevice *self, const gchar *custom_flags)
4663
0
{
4664
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
4665
4666
0
  g_return_if_fail(FU_IS_DEVICE(self));
4667
0
  g_return_if_fail(custom_flags != NULL);
4668
4669
  /* save what was set so we can use it for incorporating a superclass */
4670
0
  g_free(priv->custom_flags);
4671
0
  priv->custom_flags = g_strdup(custom_flags);
4672
4673
  /* look for any standard FwupdDeviceFlags */
4674
0
  if (custom_flags != NULL) {
4675
0
    g_auto(GStrv) hints = g_strsplit(custom_flags, ",", -1);
4676
0
    for (guint i = 0; hints[i] != NULL; i++)
4677
0
      fu_device_set_custom_flag(self, hints[i]);
4678
0
  }
4679
0
}
4680
4681
/**
4682
 * fu_device_get_custom_flags:
4683
 * @self: a #FuDevice
4684
 *
4685
 * Gets the custom flags for the device from the quirk system.
4686
 *
4687
 * Returns: a string value, or %NULL if never set.
4688
 *
4689
 * Since: 1.1.0
4690
 **/
4691
const gchar *
4692
fu_device_get_custom_flags(FuDevice *self)
4693
0
{
4694
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
4695
0
  g_return_val_if_fail(FU_IS_DEVICE(self), NULL);
4696
0
  return priv->custom_flags;
4697
0
}
4698
4699
/**
4700
 * fu_device_get_remove_delay:
4701
 * @self: a #FuDevice
4702
 *
4703
 * Returns the maximum delay expected when replugging the device going into
4704
 * bootloader mode.
4705
 *
4706
 * Returns: time in milliseconds
4707
 *
4708
 * Since: 1.0.2
4709
 **/
4710
guint
4711
fu_device_get_remove_delay(FuDevice *self)
4712
0
{
4713
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
4714
0
  g_return_val_if_fail(FU_IS_DEVICE(self), 0);
4715
0
  return priv->remove_delay;
4716
0
}
4717
4718
/**
4719
 * fu_device_set_remove_delay:
4720
 * @self: a #FuDevice
4721
 * @remove_delay: the value in milliseconds
4722
 *
4723
 * Sets the amount of time a device is allowed to return in bootloader mode.
4724
 *
4725
 * NOTE: this should be less than 3000ms for devices that just have to reset
4726
 * and automatically re-enumerate, but significantly longer if it involves a
4727
 * user removing a cable, pressing several buttons and removing a cable.
4728
 * A suggested value for this would be 10,000ms.
4729
 *
4730
 * Since: 1.0.2
4731
 **/
4732
void
4733
fu_device_set_remove_delay(FuDevice *self, guint remove_delay)
4734
0
{
4735
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
4736
0
  g_return_if_fail(FU_IS_DEVICE(self));
4737
0
  priv->remove_delay = remove_delay;
4738
0
}
4739
4740
/**
4741
 * fu_device_get_acquiesce_delay:
4742
 * @self: a #FuDevice
4743
 *
4744
 * Returns the time the daemon should wait for devices to finish hotplugging
4745
 * after the update has completed.
4746
 *
4747
 * Returns: time in milliseconds
4748
 *
4749
 * Since: 1.8.3
4750
 **/
4751
guint
4752
fu_device_get_acquiesce_delay(FuDevice *self)
4753
0
{
4754
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
4755
0
  g_return_val_if_fail(FU_IS_DEVICE(self), 0);
4756
0
  return priv->acquiesce_delay;
4757
0
}
4758
4759
/**
4760
 * fu_device_set_acquiesce_delay:
4761
 * @self: a #FuDevice
4762
 * @acquiesce_delay: the value in milliseconds
4763
 *
4764
 * Sets the time the daemon should wait for devices to finish hotplugging
4765
 * after the update has completed.
4766
 *
4767
 * Devices subclassing from [class@FuUsbDevice] and [class@FuUdevDevice] use
4768
 * a value of 2,500ms, and other devices use 50ms by default. This can be also
4769
 * be set using `AcquiesceDelay=` in a quirk file.
4770
 *
4771
 * Since: 1.8.3
4772
 **/
4773
void
4774
fu_device_set_acquiesce_delay(FuDevice *self, guint acquiesce_delay)
4775
0
{
4776
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
4777
0
  g_return_if_fail(FU_IS_DEVICE(self));
4778
0
  priv->acquiesce_delay = acquiesce_delay;
4779
0
}
4780
4781
/**
4782
 * fu_device_set_update_state:
4783
 * @self: a #FuDevice
4784
 * @update_state: the state, e.g. %FWUPD_UPDATE_STATE_PENDING
4785
 *
4786
 * Sets the update state, clearing the update error as required.
4787
 *
4788
 * Since: 1.6.2
4789
 **/
4790
void
4791
fu_device_set_update_state(FuDevice *self, FwupdUpdateState update_state)
4792
0
{
4793
0
  g_return_if_fail(FU_IS_DEVICE(self));
4794
0
  if (update_state == FWUPD_UPDATE_STATE_SUCCESS ||
4795
0
      update_state == FWUPD_UPDATE_STATE_PENDING ||
4796
0
      update_state == FWUPD_UPDATE_STATE_NEEDS_REBOOT)
4797
0
    fu_device_set_update_error(self, NULL);
4798
0
  if (update_state == FWUPD_UPDATE_STATE_NEEDS_REBOOT) {
4799
0
    fu_device_add_problem(self, FWUPD_DEVICE_PROBLEM_UPDATE_IN_PROGRESS);
4800
0
  } else {
4801
0
    fu_device_remove_problem(self, FWUPD_DEVICE_PROBLEM_UPDATE_IN_PROGRESS);
4802
0
  }
4803
0
  fwupd_device_set_update_state(FWUPD_DEVICE(self), update_state);
4804
0
}
4805
4806
static void
4807
fu_device_ensure_battery_inhibit(FuDevice *self)
4808
0
{
4809
0
  if (fu_device_get_battery_level(self) == FWUPD_BATTERY_LEVEL_INVALID ||
4810
0
      fu_device_get_battery_level(self) >= fu_device_get_battery_threshold(self)) {
4811
0
    fu_device_remove_problem(self, FWUPD_DEVICE_PROBLEM_POWER_TOO_LOW);
4812
0
    return;
4813
0
  }
4814
0
  fu_device_add_problem(self, FWUPD_DEVICE_PROBLEM_POWER_TOO_LOW);
4815
0
}
4816
4817
/**
4818
 * fu_device_get_battery_level:
4819
 * @self: a #FuDevice
4820
 *
4821
 * Returns the battery level.
4822
 *
4823
 * Returns: value in percent
4824
 *
4825
 * Since: 1.5.8
4826
 **/
4827
guint
4828
fu_device_get_battery_level(FuDevice *self)
4829
0
{
4830
0
  g_return_val_if_fail(FU_IS_DEVICE(self), G_MAXUINT);
4831
4832
  /* use the parent if the child is unset */
4833
0
  if (fu_device_has_private_flag(self, FU_DEVICE_PRIVATE_FLAG_USE_PARENT_FOR_BATTERY) &&
4834
0
      fwupd_device_get_battery_level(FWUPD_DEVICE(self)) == FWUPD_BATTERY_LEVEL_INVALID) {
4835
0
    FuDevice *parent = fu_device_get_parent(self);
4836
0
    if (parent != NULL)
4837
0
      return fu_device_get_battery_level(parent);
4838
0
  }
4839
0
  return fwupd_device_get_battery_level(FWUPD_DEVICE(self));
4840
0
}
4841
4842
/**
4843
 * fu_device_set_battery_level:
4844
 * @self: a #FuDevice
4845
 * @battery_level: the percentage value
4846
 *
4847
 * Sets the battery level, or %FWUPD_BATTERY_LEVEL_INVALID.
4848
 *
4849
 * Setting this allows fwupd to show a warning if the device change is too low
4850
 * to perform the update.
4851
 *
4852
 * Since: 1.5.8
4853
 **/
4854
void
4855
fu_device_set_battery_level(FuDevice *self, guint battery_level)
4856
0
{
4857
0
  g_return_if_fail(FU_IS_DEVICE(self));
4858
0
  g_return_if_fail(battery_level <= FWUPD_BATTERY_LEVEL_INVALID);
4859
0
  fwupd_device_set_battery_level(FWUPD_DEVICE(self), battery_level);
4860
0
  fu_device_ensure_battery_inhibit(self);
4861
0
}
4862
4863
/**
4864
 * fu_device_get_battery_threshold:
4865
 * @self: a #FuDevice
4866
 *
4867
 * Returns the battery threshold under which a firmware update cannot be
4868
 * performed.
4869
 *
4870
 * If fu_device_set_battery_threshold() has not been used, a default value is
4871
 * used instead.
4872
 *
4873
 * Returns: value in percent
4874
 *
4875
 * Since: 1.6.0
4876
 **/
4877
guint
4878
fu_device_get_battery_threshold(FuDevice *self)
4879
0
{
4880
0
  g_return_val_if_fail(FU_IS_DEVICE(self), FWUPD_BATTERY_LEVEL_INVALID);
4881
4882
  /* use the parent if the child is unset */
4883
0
  if (fu_device_has_private_flag(self, FU_DEVICE_PRIVATE_FLAG_USE_PARENT_FOR_BATTERY) &&
4884
0
      fwupd_device_get_battery_threshold(FWUPD_DEVICE(self)) == FWUPD_BATTERY_LEVEL_INVALID) {
4885
0
    FuDevice *parent = fu_device_get_parent(self);
4886
0
    if (parent != NULL)
4887
0
      return fu_device_get_battery_threshold(parent);
4888
0
  }
4889
0
  return fwupd_device_get_battery_threshold(FWUPD_DEVICE(self));
4890
0
}
4891
4892
/**
4893
 * fu_device_set_battery_threshold:
4894
 * @self: a #FuDevice
4895
 * @battery_threshold: the percentage value
4896
 *
4897
 * Sets the battery level, or %FWUPD_BATTERY_LEVEL_INVALID for the default.
4898
 *
4899
 * Setting this allows fwupd to show a warning if the device change is too low
4900
 * to perform the update.
4901
 *
4902
 * Since: 1.6.0
4903
 **/
4904
void
4905
fu_device_set_battery_threshold(FuDevice *self, guint battery_threshold)
4906
0
{
4907
0
  g_return_if_fail(FU_IS_DEVICE(self));
4908
0
  g_return_if_fail(battery_threshold <= FWUPD_BATTERY_LEVEL_INVALID);
4909
0
  fwupd_device_set_battery_threshold(FWUPD_DEVICE(self), battery_threshold);
4910
0
  fu_device_ensure_battery_inhibit(self);
4911
0
}
4912
4913
/**
4914
 * fu_device_get_created_usec:
4915
 * @self: a #FuDevice
4916
 *
4917
 * Gets when the device was created.
4918
 *
4919
 * If the usec-precision value has not been set with fu_device_set_created_usec(),
4920
 * the exported seconds-precision fallback value is returned instead.
4921
 *
4922
 * Returns: value in microseconds, or -1 for invalid
4923
 *
4924
 * Since: 2.0.0
4925
 **/
4926
gint64
4927
fu_device_get_created_usec(FuDevice *self)
4928
0
{
4929
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
4930
0
  g_return_val_if_fail(FU_IS_DEVICE(self), -1);
4931
4932
0
  if (priv->created_usec > 0)
4933
0
    return priv->created_usec;
4934
0
  return fwupd_device_get_created(FWUPD_DEVICE(self)) * G_USEC_PER_SEC;
4935
0
}
4936
4937
/**
4938
 * fu_device_set_created_usec:
4939
 * @self: a #FuDevice
4940
 * @created_usec: value in microseconds
4941
 *
4942
 * Sets when the device was created.
4943
 *
4944
 * NOTE: This also sets the seconds-precision fallback value.
4945
 *
4946
 * Since: 2.0.0
4947
 **/
4948
void
4949
fu_device_set_created_usec(FuDevice *self, gint64 created_usec)
4950
0
{
4951
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
4952
0
  g_return_if_fail(FU_IS_DEVICE(self));
4953
0
  g_return_if_fail(created_usec == 0 || created_usec > 10000000000);
4954
4955
0
  priv->created_usec = created_usec;
4956
0
  fwupd_device_set_created(FWUPD_DEVICE(self), created_usec / G_USEC_PER_SEC);
4957
0
}
4958
4959
/**
4960
 * fu_device_get_modified_usec:
4961
 * @self: a #FuDevice
4962
 *
4963
 * Gets when the device was modified.
4964
 *
4965
 * If the usec-precision value has not been set with fu_device_set_modified_usec(),
4966
 * the exported seconds-precision fallback value is returned instead.
4967
 *
4968
 * Returns: value in microseconds, or -1 for invalid
4969
 *
4970
 * Since: 2.0.0
4971
 **/
4972
gint64
4973
fu_device_get_modified_usec(FuDevice *self)
4974
0
{
4975
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
4976
0
  g_return_val_if_fail(FU_IS_DEVICE(self), -1);
4977
4978
0
  if (priv->modified_usec > 0)
4979
0
    return priv->modified_usec;
4980
0
  return fwupd_device_get_modified(FWUPD_DEVICE(self)) * G_USEC_PER_SEC;
4981
0
}
4982
4983
/**
4984
 * fu_device_get_vid:
4985
 * @self: a #FuUdevDevice
4986
 *
4987
 * Gets the device vendor code.
4988
 *
4989
 * Returns: a vendor code, or 0 if unset or invalid
4990
 *
4991
 * Since: 2.0.0
4992
 **/
4993
guint16
4994
fu_device_get_vid(FuDevice *self)
4995
0
{
4996
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
4997
0
  g_return_val_if_fail(FU_IS_DEVICE(self), 0x0000);
4998
0
  return priv->vid;
4999
0
}
5000
5001
/**
5002
 * fu_device_set_vid:
5003
 * @self: a #FuUdevDevice
5004
 * @vid: an integer ID
5005
 *
5006
 * Sets the vendor ID.
5007
 *
5008
 * Since: 2.0.0
5009
 **/
5010
void
5011
fu_device_set_vid(FuDevice *self, guint16 vid)
5012
0
{
5013
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
5014
0
  g_return_if_fail(FU_IS_DEVICE(self));
5015
0
  if (priv->vid == vid)
5016
0
    return;
5017
0
  priv->vid = vid;
5018
0
  g_object_notify(G_OBJECT(self), "vid");
5019
0
}
5020
5021
/**
5022
 * fu_device_get_pid:
5023
 * @self: a #FuUdevDevice
5024
 *
5025
 * Gets the device product code.
5026
 *
5027
 * Returns: a product code, or 0 if unset or invalid
5028
 *
5029
 * Since: 2.0.0
5030
 **/
5031
guint16
5032
fu_device_get_pid(FuDevice *self)
5033
0
{
5034
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
5035
0
  g_return_val_if_fail(FU_IS_DEVICE(self), 0x0000);
5036
0
  return priv->pid;
5037
0
}
5038
5039
/**
5040
 * fu_device_set_pid:
5041
 * @self: a #FuUdevDevice
5042
 * @pid: an integer ID
5043
 *
5044
 * Sets the product ID.
5045
 *
5046
 * Since: 2.0.0
5047
 **/
5048
void
5049
fu_device_set_pid(FuDevice *self, guint16 pid)
5050
0
{
5051
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
5052
0
  g_return_if_fail(FU_IS_DEVICE(self));
5053
0
  if (priv->pid == pid)
5054
0
    return;
5055
0
  priv->pid = pid;
5056
0
  g_object_notify(G_OBJECT(self), "pid");
5057
0
}
5058
5059
/**
5060
 * fu_device_set_modified_usec:
5061
 * @self: a #FuDevice
5062
 * @modified_usec: value in microseconds
5063
 *
5064
 * Sets when the device was modified.
5065
 *
5066
 * NOTE: This also sets the seconds-precision fallback value.
5067
 *
5068
 * Since: 2.0.0
5069
 **/
5070
void
5071
fu_device_set_modified_usec(FuDevice *self, gint64 modified_usec)
5072
0
{
5073
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
5074
0
  g_return_if_fail(FU_IS_DEVICE(self));
5075
0
  g_return_if_fail(modified_usec == 0 || modified_usec > 10000000000);
5076
5077
0
  priv->modified_usec = modified_usec;
5078
0
  fwupd_device_set_modified(FWUPD_DEVICE(self), modified_usec / G_USEC_PER_SEC);
5079
0
}
5080
5081
static gchar *
5082
fu_device_instance_flag_to_string_trunc(FuDeviceInstanceFlags flags)
5083
0
{
5084
0
  g_autofree gchar *tmp = fu_device_instance_flags_to_string(flags);
5085
0
  g_auto(GStrv) split = g_strsplit(tmp, ",", -1);
5086
0
  for (guint i = 0; split[i] != NULL; i++) {
5087
0
    if (strlen(split[i]) > 2)
5088
0
      split[i][2] = '\0';
5089
0
  }
5090
0
  return g_strjoinv(",", split);
5091
0
}
5092
5093
static void
5094
fu_device_to_string_impl(FuDevice *self, guint idt, GString *str)
5095
0
{
5096
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
5097
5098
0
  for (guint i = 0; priv->instance_ids != NULL && i < priv->instance_ids->len; i++) {
5099
0
    FuDeviceInstanceIdItem *item = g_ptr_array_index(priv->instance_ids, i);
5100
0
    g_autofree gchar *flags_str = fu_device_instance_flag_to_string_trunc(item->flags);
5101
0
    g_autofree gchar *title = g_strdup_printf("InstanceId[%s]", flags_str);
5102
0
    if (item->instance_id != NULL) {
5103
0
      g_autofree gchar *tmp2 =
5104
0
          g_strdup_printf("%s ← %s", item->guid, item->instance_id);
5105
0
      fwupd_codec_string_append(str, idt, title, tmp2);
5106
0
    } else {
5107
0
      fwupd_codec_string_append(str, idt, title, item->guid);
5108
0
    }
5109
0
  }
5110
0
  fwupd_codec_string_append(str, idt, "EquivalentId", priv->equivalent_id);
5111
0
  fwupd_codec_string_append(str, idt, "PhysicalId", priv->physical_id);
5112
0
  fwupd_codec_string_append(str, idt, "LogicalId", priv->logical_id);
5113
0
  fwupd_codec_string_append(str, idt, "BackendId", priv->backend_id);
5114
0
  fwupd_codec_string_append_hex(str, idt, "Vid", priv->vid);
5115
0
  fwupd_codec_string_append_hex(str, idt, "Pid", priv->pid);
5116
0
  fwupd_codec_string_append(str, idt, "UpdateRequestId", priv->update_request_id);
5117
0
  fwupd_codec_string_append(str, idt, FWUPD_RESULT_KEY_UPDATE_MESSAGE, priv->update_message);
5118
0
  fwupd_codec_string_append(str, idt, FWUPD_RESULT_KEY_UPDATE_IMAGE, priv->update_image);
5119
0
  fwupd_codec_string_append(str, idt, "FwupdVersion", priv->fwupd_version);
5120
0
  fwupd_codec_string_append(str, idt, "ProxyGuid", priv->proxy_guid);
5121
0
  fwupd_codec_string_append_int(str, idt, "RemoveDelay", priv->remove_delay);
5122
0
  fwupd_codec_string_append_int(str, idt, "AcquiesceDelay", priv->acquiesce_delay);
5123
0
  fwupd_codec_string_append(str, idt, "CustomFlags", priv->custom_flags);
5124
0
  if (priv->specialized_gtype != G_TYPE_INVALID)
5125
0
    fwupd_codec_string_append(str, idt, "GType", g_type_name(priv->specialized_gtype));
5126
0
  if (priv->proxy_gtype != G_TYPE_INVALID)
5127
0
    fwupd_codec_string_append(str, idt, "ProxyGType", g_type_name(priv->proxy_gtype));
5128
0
  if (priv->firmware_gtype != G_TYPE_INVALID) {
5129
0
    fwupd_codec_string_append(str,
5130
0
            idt,
5131
0
            "FirmwareGType",
5132
0
            g_type_name(priv->firmware_gtype));
5133
0
  }
5134
0
  fwupd_codec_string_append_size(str, idt, "FirmwareSizeMin", priv->size_min);
5135
0
  fwupd_codec_string_append_size(str, idt, "FirmwareSizeMax", priv->size_max);
5136
0
  fwupd_codec_string_append_int(str, idt, "RequiredFree", priv->required_free);
5137
0
  if (priv->order != G_MAXINT) {
5138
0
    g_autofree gchar *order = g_strdup_printf("%i", priv->order);
5139
0
    fwupd_codec_string_append(str, idt, "Order", order);
5140
0
  }
5141
0
  fwupd_codec_string_append_int(str, idt, "Priority", priv->priority);
5142
0
  if (priv->metadata != NULL) {
5143
0
    g_autoptr(GList) keys = g_hash_table_get_keys(priv->metadata);
5144
0
    for (GList *l = keys; l != NULL; l = l->next) {
5145
0
      const gchar *key = l->data;
5146
0
      const gchar *value = g_hash_table_lookup(priv->metadata, key);
5147
0
      fwupd_codec_string_append(str, idt, key, value);
5148
0
    }
5149
0
  }
5150
0
  for (guint i = 0; i < priv->possible_plugins->len; i++) {
5151
0
    const gchar *name = g_ptr_array_index(priv->possible_plugins, i);
5152
0
    fwupd_codec_string_append(str, idt, "PossiblePlugin", name);
5153
0
  }
5154
0
  if (priv->parent_physical_ids != NULL && priv->parent_physical_ids->len > 0) {
5155
0
    g_autofree gchar *flags = fu_strjoin(",", priv->parent_physical_ids);
5156
0
    fwupd_codec_string_append(str, idt, "ParentPhysicalIds", flags);
5157
0
  }
5158
0
  if (priv->parent_backend_ids != NULL && priv->parent_backend_ids->len > 0) {
5159
0
    g_autofree gchar *flags = fu_strjoin(",", priv->parent_backend_ids);
5160
0
    fwupd_codec_string_append(str, idt, "ParentBackendIds", flags);
5161
0
  }
5162
0
  if (priv->private_flags != NULL && priv->private_flags->len != 0) {
5163
0
    g_autoptr(GPtrArray) tmpv = g_ptr_array_new();
5164
0
    for (guint64 i = 0; i < priv->private_flags->len; i++) {
5165
0
      GQuark flag_quark = g_array_index(priv->private_flags, GQuark, i);
5166
0
      g_ptr_array_add(tmpv, (gpointer)g_quark_to_string(flag_quark));
5167
0
    }
5168
0
    if (tmpv->len > 0) {
5169
0
      g_autofree gchar *tmps = fu_strjoin(",", tmpv);
5170
0
      fwupd_codec_string_append(str, idt, "PrivateFlags", tmps);
5171
0
    }
5172
0
  }
5173
0
  if (priv->instance_hash != NULL) {
5174
0
    GHashTableIter iter;
5175
0
    gpointer key, value;
5176
0
    g_hash_table_iter_init(&iter, priv->instance_hash);
5177
0
    while (g_hash_table_iter_next(&iter, &key, &value)) {
5178
0
      g_autofree gchar *title =
5179
0
          g_strdup_printf("InstanceKey[%s]", (const gchar *)key);
5180
0
      fwupd_codec_string_append(str, idt, title, value);
5181
0
    }
5182
0
  }
5183
0
  if (priv->inhibits != NULL) {
5184
0
    g_autoptr(GList) values = g_hash_table_get_values(priv->inhibits);
5185
0
    for (GList *l = values; l != NULL; l = l->next) {
5186
0
      FuDeviceInhibit *inhibit = (FuDeviceInhibit *)l->data;
5187
0
      g_autofree gchar *val =
5188
0
          g_strdup_printf("[%s] %s", inhibit->inhibit_id, inhibit->reason);
5189
0
      fwupd_codec_string_append(str, idt, "Inhibit", val);
5190
0
    }
5191
0
  }
5192
0
  if (priv->events != NULL) {
5193
0
    fwupd_codec_string_append(str, idt, "Events", "");
5194
0
    for (guint i = 0; i < priv->events->len; i++) {
5195
0
      FuDeviceEvent *event = g_ptr_array_index(priv->events, i);
5196
0
      if (i > 10) {
5197
0
        g_autofree gchar *msg =
5198
0
            g_strdup_printf("…and %u more events", priv->events->len - 10);
5199
0
        fwupd_codec_string_append(str, idt + 1, "", msg);
5200
0
        break;
5201
0
      }
5202
0
      fwupd_codec_add_string(FWUPD_CODEC(event), idt + 1, str);
5203
0
    }
5204
0
  }
5205
0
  if (priv->proxy != NULL) {
5206
0
    fwupd_codec_string_append(str, idt, "Proxy", "");
5207
0
    fu_device_to_string_impl(priv->proxy, idt + 1, str);
5208
0
  }
5209
0
}
5210
5211
static GPtrArray *
5212
fu_device_get_common_class_parents(FuDevice *self, FuDevice *donor)
5213
0
{
5214
0
  g_autoptr(GPtrArray) array = g_ptr_array_new();
5215
0
  for (GType gtype = G_OBJECT_TYPE(self); gtype != FWUPD_TYPE_DEVICE;
5216
0
       gtype = g_type_parent(gtype)) {
5217
0
    FuDeviceClass *device_class = g_type_class_peek(gtype);
5218
0
    for (GType gtype_donor = G_OBJECT_TYPE(donor); gtype_donor != FWUPD_TYPE_DEVICE;
5219
0
         gtype_donor = g_type_parent(gtype_donor)) {
5220
0
      if (gtype == gtype_donor)
5221
0
        g_ptr_array_add(array, device_class);
5222
0
    }
5223
0
  }
5224
0
  return g_steal_pointer(&array);
5225
0
}
5226
5227
/**
5228
 * fu_device_add_string:
5229
 * @self: a #FuDevice
5230
 * @idt: indent level
5231
 * @str: a string to append to
5232
 *
5233
 * Add daemon-specific device metadata to an existing string.
5234
 *
5235
 * Since: 1.7.1
5236
 **/
5237
void
5238
fu_device_add_string(FuDevice *self, guint idt, GString *str)
5239
0
{
5240
0
  GPtrArray *children;
5241
0
  gpointer device_class_to_string_last = NULL;
5242
0
  g_autoptr(GList) device_class_list = NULL;
5243
5244
  /* add baseclass */
5245
0
  fwupd_codec_add_string(FWUPD_CODEC(self), idt, str);
5246
5247
  /* run every unique ->to_string() in each subclass */
5248
0
  for (GType gtype = G_OBJECT_TYPE(self); gtype != G_TYPE_INVALID;
5249
0
       gtype = g_type_parent(gtype)) {
5250
0
    FuDeviceClass *device_class = g_type_class_peek(gtype);
5251
0
    if (!FU_IS_DEVICE_CLASS(device_class))
5252
0
      break;
5253
0
    device_class_list = g_list_prepend(device_class_list, device_class);
5254
0
  }
5255
0
  for (GList *l = device_class_list; l != NULL; l = l->next) {
5256
0
    FuDeviceClass *device_class = FU_DEVICE_CLASS(l->data);
5257
0
    if (device_class->to_string != NULL &&
5258
0
        device_class->to_string != device_class_to_string_last) {
5259
0
      device_class->to_string(self, idt + 1, str);
5260
0
      device_class_to_string_last = device_class->to_string;
5261
0
    }
5262
0
  }
5263
5264
  /* print children also */
5265
0
  children = fu_device_get_children(self);
5266
0
  for (guint i = 0; i < children->len; i++) {
5267
0
    FuDevice *child = g_ptr_array_index(children, i);
5268
0
    fu_device_add_string(child, idt + 1, str);
5269
0
  }
5270
0
}
5271
5272
/**
5273
 * fu_device_to_string:
5274
 * @self: a #FuDevice
5275
 *
5276
 * This allows us to easily print the device, the release and the
5277
 * daemon-specific metadata.
5278
 *
5279
 * Returns: a string value, or %NULL for invalid.
5280
 *
5281
 * Since: 0.9.8
5282
 **/
5283
gchar *
5284
fu_device_to_string(FuDevice *self)
5285
0
{
5286
0
  GString *str = g_string_new(NULL);
5287
0
  fu_device_add_string(self, 0, str);
5288
0
  return g_string_free(str, FALSE);
5289
0
}
5290
5291
/**
5292
 * fu_device_set_context:
5293
 * @self: a #FuDevice
5294
 * @ctx: (nullable): optional #FuContext
5295
 *
5296
 * Sets the optional context which may be useful to this device.
5297
 * This is typically set after the device has been created, but before
5298
 * the device has been opened or probed.
5299
 *
5300
 * Since: 1.6.0
5301
 **/
5302
void
5303
fu_device_set_context(FuDevice *self, FuContext *ctx)
5304
0
{
5305
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
5306
0
  g_return_if_fail(FU_IS_DEVICE(self));
5307
0
  g_return_if_fail(FU_IS_CONTEXT(ctx) || ctx == NULL);
5308
5309
0
#ifndef SUPPORTED_BUILD
5310
0
  if (priv->ctx != NULL && ctx == NULL) {
5311
0
    g_critical("clearing device context for %s [%s]",
5312
0
         fu_device_get_name(self),
5313
0
         fu_device_get_id(self));
5314
0
    return;
5315
0
  }
5316
0
#endif
5317
5318
0
  if (g_set_object(&priv->ctx, ctx))
5319
0
    g_object_notify(G_OBJECT(self), "context");
5320
0
}
5321
5322
/**
5323
 * fu_device_get_context:
5324
 * @self: a #FuDevice
5325
 *
5326
 * Gets the context assigned for this device.
5327
 *
5328
 * Returns: (transfer none): the #FuContext object, or %NULL
5329
 *
5330
 * Since: 1.6.0
5331
 **/
5332
FuContext *
5333
fu_device_get_context(FuDevice *self)
5334
0
{
5335
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
5336
0
  g_return_val_if_fail(FU_IS_DEVICE(self), NULL);
5337
0
  return priv->ctx;
5338
0
}
5339
5340
/**
5341
 * fu_device_get_results:
5342
 * @self: a #FuDevice
5343
 * @error: (nullable): optional return location for an error
5344
 *
5345
 * Gets the results of the last update operation on the device by calling a vfunc.
5346
 *
5347
 * Returns: %TRUE on success
5348
 *
5349
 * Since: 1.6.2
5350
 **/
5351
gboolean
5352
fu_device_get_results(FuDevice *self, GError **error)
5353
0
{
5354
0
  FuDeviceClass *device_class = FU_DEVICE_GET_CLASS(self);
5355
5356
0
  g_return_val_if_fail(FU_IS_DEVICE(self), FALSE);
5357
0
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
5358
5359
  /* no plugin-specific method */
5360
0
  if (device_class->get_results == NULL) {
5361
0
    g_set_error_literal(error,
5362
0
            FWUPD_ERROR,
5363
0
            FWUPD_ERROR_NOT_SUPPORTED,
5364
0
            "getting results not supported by device");
5365
0
    return FALSE;
5366
0
  }
5367
5368
  /* call vfunc */
5369
0
  return device_class->get_results(self, error);
5370
0
}
5371
5372
/**
5373
 * fu_device_write_firmware:
5374
 * @self: a #FuDevice
5375
 * @firmware: a #FuFirmware
5376
 * @progress: a #FuProgress
5377
 * @flags: install flags, e.g. %FWUPD_INSTALL_FLAG_FORCE
5378
 * @error: (nullable): optional return location for an error
5379
 *
5380
 * Writes firmware to the device by calling a plugin-specific vfunc.
5381
 *
5382
 * Returns: %TRUE on success
5383
 *
5384
 * Since: 2.0.7
5385
 **/
5386
gboolean
5387
fu_device_write_firmware(FuDevice *self,
5388
       FuFirmware *firmware,
5389
       FuProgress *progress,
5390
       FwupdInstallFlags flags,
5391
       GError **error)
5392
0
{
5393
0
  FuDeviceClass *device_class = FU_DEVICE_GET_CLASS(self);
5394
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
5395
0
  g_autofree gchar *str = NULL;
5396
5397
0
  g_return_val_if_fail(FU_IS_DEVICE(self), FALSE);
5398
0
  g_return_val_if_fail(FU_IS_FIRMWARE(firmware), FALSE);
5399
0
  g_return_val_if_fail(FU_IS_PROGRESS(progress), FALSE);
5400
0
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
5401
5402
  /* no plugin-specific method */
5403
0
  if (device_class->write_firmware == NULL) {
5404
0
    g_set_error_literal(error,
5405
0
            FWUPD_ERROR,
5406
0
            FWUPD_ERROR_NOT_SUPPORTED,
5407
0
            "writing firmware not supported by device");
5408
0
    return FALSE;
5409
0
  }
5410
5411
  /* call vfunc */
5412
0
  str = fu_firmware_to_string(firmware);
5413
0
  g_info("installing onto %s:\n%s", fu_device_get_id(self), str);
5414
0
  g_set_object(&priv->progress, progress);
5415
0
  if (!device_class->write_firmware(self, firmware, priv->progress, flags, error))
5416
0
    return FALSE;
5417
5418
  /* the device set an UpdateMessage (possibly from a quirk, or XML file)
5419
   * but did not do an event; guess something */
5420
0
  if (priv->request_cnts[FWUPD_REQUEST_KIND_POST] == 0 &&
5421
0
      fu_device_get_update_message(self) != NULL) {
5422
0
    const gchar *update_request_id = fu_device_get_update_request_id(self);
5423
0
    g_autoptr(FwupdRequest) request = fwupd_request_new();
5424
0
    fwupd_request_set_kind(request, FWUPD_REQUEST_KIND_POST);
5425
0
    if (update_request_id != NULL) {
5426
0
      fwupd_request_set_id(request, update_request_id);
5427
0
      fwupd_request_add_flag(request, FWUPD_REQUEST_FLAG_ALLOW_GENERIC_MESSAGE);
5428
0
    } else {
5429
0
      fu_device_add_request_flag(self, FWUPD_REQUEST_FLAG_NON_GENERIC_MESSAGE);
5430
0
      fwupd_request_set_id(request, FWUPD_REQUEST_ID_REMOVE_REPLUG);
5431
0
    }
5432
0
    fwupd_request_set_message(request, fu_device_get_update_message(self));
5433
0
    fwupd_request_set_image(request, fu_device_get_update_image(self));
5434
0
    if (!fu_device_emit_request(self, request, progress, error))
5435
0
      return FALSE;
5436
0
  }
5437
5438
  /* success */
5439
0
  return TRUE;
5440
0
}
5441
5442
/**
5443
 * fu_device_prepare_firmware:
5444
 * @self: a #FuDevice
5445
 * @stream: a #GInputStream
5446
 * @flags: #FuFirmwareParseFlags, e.g. %FWUPD_INSTALL_FLAG_FORCE
5447
 * @error: (nullable): optional return location for an error
5448
 *
5449
 * Prepares the firmware by calling an optional device-specific vfunc for the
5450
 * device, which can do things like decompressing or parsing of the firmware
5451
 * data.
5452
 *
5453
 * For all firmware, this checks the size of the firmware if limits have been
5454
 * set using fu_device_set_firmware_size_min(), fu_device_set_firmware_size_max()
5455
 * or using a quirk entry.
5456
 *
5457
 * Returns: (transfer full): a new #GBytes, or %NULL for error
5458
 *
5459
 * Since: 1.1.2
5460
 **/
5461
FuFirmware *
5462
fu_device_prepare_firmware(FuDevice *self,
5463
         GInputStream *stream,
5464
         FuProgress *progress,
5465
         FuFirmwareParseFlags flags,
5466
         GError **error)
5467
0
{
5468
0
  FuDeviceClass *device_class = FU_DEVICE_GET_CLASS(self);
5469
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
5470
0
  g_autoptr(FuFirmware) firmware = NULL;
5471
0
  gsize fw_size;
5472
5473
0
  g_return_val_if_fail(FU_IS_DEVICE(self), NULL);
5474
0
  g_return_val_if_fail(G_IS_INPUT_STREAM(stream), NULL);
5475
0
  g_return_val_if_fail(FU_IS_PROGRESS(progress), NULL);
5476
0
  g_return_val_if_fail(error == NULL || *error == NULL, NULL);
5477
5478
  /* optionally subclassed */
5479
0
  if (device_class->prepare_firmware != NULL) {
5480
0
    firmware = device_class->prepare_firmware(self, stream, progress, flags, error);
5481
0
    if (firmware == NULL)
5482
0
      return NULL;
5483
0
  } else if (priv->firmware_gtype != G_TYPE_INVALID) {
5484
0
    firmware = g_object_new(priv->firmware_gtype, NULL);
5485
0
    if (!fu_firmware_parse_stream(firmware, stream, 0x0, flags, error))
5486
0
      return NULL;
5487
0
  } else {
5488
0
    firmware = fu_firmware_new();
5489
0
    if (!fu_firmware_parse_stream(firmware, stream, 0x0, flags, error))
5490
0
      return NULL;
5491
0
  }
5492
5493
  /* check size */
5494
0
  fw_size = fu_firmware_get_size(firmware);
5495
0
  if (fw_size != 0) {
5496
0
    if (priv->size_max > 0 && fw_size > priv->size_max) {
5497
0
      g_set_error(error,
5498
0
            FWUPD_ERROR,
5499
0
            FWUPD_ERROR_INVALID_FILE,
5500
0
            "firmware is 0x%04x bytes larger than the allowed "
5501
0
            "maximum size of 0x%04x bytes",
5502
0
            (guint)(fw_size - priv->size_max),
5503
0
            (guint)priv->size_max);
5504
0
      return NULL;
5505
0
    }
5506
0
    if (priv->size_min > 0 && fw_size < priv->size_min) {
5507
0
      g_set_error(error,
5508
0
            FWUPD_ERROR,
5509
0
            FWUPD_ERROR_INVALID_FILE,
5510
0
            "firmware is %04x bytes smaller than the allowed "
5511
0
            "minimum size of %04x bytes",
5512
0
            (guint)(priv->size_min - fw_size),
5513
0
            (guint)priv->size_max);
5514
0
      return NULL;
5515
0
    }
5516
0
  }
5517
5518
  /* success */
5519
0
  return g_steal_pointer(&firmware);
5520
0
}
5521
5522
/**
5523
 * fu_device_read_firmware:
5524
 * @self: a #FuDevice
5525
 * @progress: a #FuProgress
5526
 * @flags: #FuFirmwareParseFlags, e.g. %FU_FIRMWARE_PARSE_FLAG_NONE
5527
 * @error: (nullable): optional return location for an error
5528
 *
5529
 * Reads firmware from the device by calling a plugin-specific vfunc.
5530
 * The device subclass should try to ensure the firmware does not contain any
5531
 * serial numbers or user-configuration values and can be used to calculate the
5532
 * device checksum.
5533
 *
5534
 * The return value can be converted to a blob of memory using fu_firmware_write().
5535
 *
5536
 * Returns: (transfer full): a #FuFirmware, or %NULL for error
5537
 *
5538
 * Since: 2.0.11, although a simpler version was added in 1.0.8
5539
 **/
5540
FuFirmware *
5541
fu_device_read_firmware(FuDevice *self,
5542
      FuProgress *progress,
5543
      FuFirmwareParseFlags flags,
5544
      GError **error)
5545
0
{
5546
0
  FuDeviceClass *device_class = FU_DEVICE_GET_CLASS(self);
5547
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
5548
0
  g_autoptr(GBytes) fw = NULL;
5549
5550
0
  g_return_val_if_fail(FU_IS_DEVICE(self), NULL);
5551
0
  g_return_val_if_fail(FU_IS_PROGRESS(progress), NULL);
5552
0
  g_return_val_if_fail(error == NULL || *error == NULL, NULL);
5553
5554
  /* call vfunc */
5555
0
  g_set_object(&priv->progress, progress);
5556
0
  if (device_class->read_firmware != NULL)
5557
0
    return device_class->read_firmware(self, progress, error);
5558
5559
  /* use the default FuFirmware when only ->dump_firmware is provided */
5560
0
  fw = fu_device_dump_firmware(self, progress, error);
5561
0
  if (fw == NULL)
5562
0
    return NULL;
5563
0
  if (priv->firmware_gtype != G_TYPE_INVALID) {
5564
0
    g_autoptr(FuFirmware) firmware = g_object_new(priv->firmware_gtype, NULL);
5565
0
    if (!fu_firmware_parse_bytes(firmware, fw, 0x0, flags, error))
5566
0
      return NULL;
5567
0
    return g_steal_pointer(&firmware);
5568
0
  }
5569
0
  return fu_firmware_new_from_bytes(fw);
5570
0
}
5571
5572
/**
5573
 * fu_device_dump_firmware:
5574
 * @self: a #FuDevice
5575
 * @progress: a #FuProgress
5576
 * @error: (nullable): optional return location for an error
5577
 *
5578
 * Reads the raw firmware image from the device by calling a plugin-specific
5579
 * vfunc. This raw firmware image may contain serial numbers or device-specific
5580
 * configuration but should be a byte-for-byte match compared to using an
5581
 * external SPI programmer.
5582
 *
5583
 * Returns: (transfer full): a #GBytes, or %NULL for error
5584
 *
5585
 * Since: 1.5.0
5586
 **/
5587
GBytes *
5588
fu_device_dump_firmware(FuDevice *self, FuProgress *progress, GError **error)
5589
0
{
5590
0
  FuDeviceClass *device_class = FU_DEVICE_GET_CLASS(self);
5591
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
5592
5593
0
  g_return_val_if_fail(FU_IS_DEVICE(self), NULL);
5594
0
  g_return_val_if_fail(FU_IS_PROGRESS(progress), NULL);
5595
0
  g_return_val_if_fail(error == NULL || *error == NULL, NULL);
5596
5597
  /* use the default FuFirmware when only ->dump_firmware is provided */
5598
0
  if (device_class->dump_firmware == NULL) {
5599
0
    g_set_error_literal(error,
5600
0
            FWUPD_ERROR,
5601
0
            FWUPD_ERROR_NOT_SUPPORTED,
5602
0
            "dumping firmware is not supported by device");
5603
0
    return NULL;
5604
0
  }
5605
5606
  /* proxy */
5607
0
  g_set_object(&priv->progress, progress);
5608
0
  return device_class->dump_firmware(self, progress, error);
5609
0
}
5610
5611
/**
5612
 * fu_device_detach:
5613
 * @self: a #FuDevice
5614
 * @error: (nullable): optional return location for an error
5615
 *
5616
 * Detaches a device from the application into bootloader mode.
5617
 *
5618
 * Returns: %TRUE on success
5619
 *
5620
 * Since: 1.0.8
5621
 **/
5622
gboolean
5623
fu_device_detach(FuDevice *self, GError **error)
5624
0
{
5625
0
  g_autoptr(FuProgress) progress = fu_progress_new(G_STRLOC);
5626
0
  return fu_device_detach_full(self, progress, error);
5627
0
}
5628
5629
/**
5630
 * fu_device_detach_full:
5631
 * @self: a #FuDevice
5632
 * @progress: a #FuProgress
5633
 * @error: (nullable): optional return location for an error
5634
 *
5635
 * Detaches a device from the application into bootloader mode.
5636
 *
5637
 * Returns: %TRUE on success
5638
 *
5639
 * Since: 1.7.0
5640
 **/
5641
gboolean
5642
fu_device_detach_full(FuDevice *self, FuProgress *progress, GError **error)
5643
0
{
5644
0
  FuDeviceClass *device_class = FU_DEVICE_GET_CLASS(self);
5645
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
5646
5647
0
  g_return_val_if_fail(FU_IS_DEVICE(self), FALSE);
5648
0
  g_return_val_if_fail(FU_IS_PROGRESS(progress), FALSE);
5649
0
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
5650
5651
  /* no plugin-specific method */
5652
0
  if (device_class->detach == NULL)
5653
0
    return TRUE;
5654
5655
  /* call vfunc */
5656
0
  g_set_object(&priv->progress, progress);
5657
0
  return device_class->detach(self, progress, error);
5658
0
}
5659
5660
/**
5661
 * fu_device_attach:
5662
 * @self: a #FuDevice
5663
 * @error: (nullable): optional return location for an error
5664
 *
5665
 * Attaches a device from the bootloader into application mode.
5666
 *
5667
 * Returns: %TRUE on success
5668
 *
5669
 * Since: 1.0.8
5670
 **/
5671
gboolean
5672
fu_device_attach(FuDevice *self, GError **error)
5673
0
{
5674
0
  g_autoptr(FuProgress) progress = fu_progress_new(G_STRLOC);
5675
0
  return fu_device_attach_full(self, progress, error);
5676
0
}
5677
5678
/**
5679
 * fu_device_attach_full:
5680
 * @self: a #FuDevice
5681
 * @progress: a #FuProgress
5682
 * @error: (nullable): optional return location for an error
5683
 *
5684
 * Attaches a device from the bootloader into application mode.
5685
 *
5686
 * Returns: %TRUE on success
5687
 *
5688
 * Since: 1.7.0
5689
 **/
5690
gboolean
5691
fu_device_attach_full(FuDevice *self, FuProgress *progress, GError **error)
5692
0
{
5693
0
  FuDeviceClass *device_class = FU_DEVICE_GET_CLASS(self);
5694
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
5695
5696
0
  g_return_val_if_fail(FU_IS_DEVICE(self), FALSE);
5697
0
  g_return_val_if_fail(FU_IS_PROGRESS(progress), FALSE);
5698
0
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
5699
5700
  /* no plugin-specific method */
5701
0
  if (device_class->attach == NULL)
5702
0
    return TRUE;
5703
5704
  /* call vfunc */
5705
0
  g_set_object(&priv->progress, progress);
5706
0
  return device_class->attach(self, progress, error);
5707
0
}
5708
5709
/**
5710
 * fu_device_reload:
5711
 * @self: a #FuDevice
5712
 * @error: (nullable): optional return location for an error
5713
 *
5714
 * Reloads a device that has just gone from bootloader into application mode.
5715
 *
5716
 * Returns: %TRUE on success
5717
 *
5718
 * Since: 1.3.3
5719
 **/
5720
gboolean
5721
fu_device_reload(FuDevice *self, GError **error)
5722
0
{
5723
0
  FuDeviceClass *device_class = FU_DEVICE_GET_CLASS(self);
5724
5725
0
  g_return_val_if_fail(FU_IS_DEVICE(self), FALSE);
5726
0
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
5727
5728
  /* no plugin-specific method */
5729
0
  if (device_class->reload == NULL)
5730
0
    return TRUE;
5731
5732
  /* call vfunc */
5733
0
  return device_class->reload(self, error);
5734
0
}
5735
5736
/**
5737
 * fu_device_prepare:
5738
 * @self: a #FuDevice
5739
 * @progress: a #FuProgress
5740
 * @flags: install flags
5741
 * @error: (nullable): optional return location for an error
5742
 *
5743
 * Prepares a device for update. A different plugin can handle each of
5744
 * FuDevice->prepare(), FuDevice->detach() and FuDevice->write_firmware().
5745
 *
5746
 * Returns: %TRUE on success
5747
 *
5748
 * Since: 1.3.3
5749
 **/
5750
gboolean
5751
fu_device_prepare(FuDevice *self, FuProgress *progress, FwupdInstallFlags flags, GError **error)
5752
0
{
5753
0
  FuDeviceClass *device_class = FU_DEVICE_GET_CLASS(self);
5754
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
5755
5756
0
  g_return_val_if_fail(FU_IS_DEVICE(self), FALSE);
5757
0
  g_return_val_if_fail(FU_IS_PROGRESS(progress), FALSE);
5758
0
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
5759
5760
  /* no plugin-specific method */
5761
0
  if (device_class->prepare == NULL)
5762
0
    return TRUE;
5763
5764
  /* call vfunc */
5765
0
  g_set_object(&priv->progress, progress);
5766
0
  return device_class->prepare(self, progress, flags, error);
5767
0
}
5768
5769
/**
5770
 * fu_device_cleanup:
5771
 * @self: a #FuDevice
5772
 * @progress: a #FuProgress
5773
 * @flags: install flags
5774
 * @error: (nullable): optional return location for an error
5775
 *
5776
 * Cleans up a device after an update. A different plugin can handle each of
5777
 * FuDevice->write_firmware(), FuDevice->attach() and FuDevice->cleanup().
5778
 *
5779
 * Returns: %TRUE on success
5780
 *
5781
 * Since: 1.3.3
5782
 **/
5783
gboolean
5784
fu_device_cleanup(FuDevice *self, FuProgress *progress, FwupdInstallFlags flags, GError **error)
5785
0
{
5786
0
  FuDeviceClass *device_class = FU_DEVICE_GET_CLASS(self);
5787
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
5788
5789
0
  g_return_val_if_fail(FU_IS_DEVICE(self), FALSE);
5790
0
  g_return_val_if_fail(FU_IS_PROGRESS(progress), FALSE);
5791
0
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
5792
5793
  /* no plugin-specific method */
5794
0
  if (device_class->cleanup == NULL)
5795
0
    return TRUE;
5796
5797
  /* call vfunc */
5798
0
  g_set_object(&priv->progress, progress);
5799
0
  return device_class->cleanup(self, progress, flags, error);
5800
0
}
5801
5802
static gboolean
5803
fu_device_open_cb(FuDevice *self, gpointer user_data, GError **error)
5804
0
{
5805
0
  FuDeviceClass *device_class = FU_DEVICE_GET_CLASS(self);
5806
0
  return device_class->open(self, error);
5807
0
}
5808
5809
static gboolean
5810
fu_device_open_internal(FuDevice *self, GError **error)
5811
0
{
5812
0
  FuDeviceClass *device_class = FU_DEVICE_GET_CLASS(self);
5813
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
5814
5815
  /* already open */
5816
0
  g_atomic_int_inc(&priv->open_refcount);
5817
0
  if (priv->open_refcount > 1)
5818
0
    return TRUE;
5819
5820
  /* probe */
5821
0
  if (!fu_device_probe(self, error)) {
5822
0
    g_prefix_error(error, "failed to probe: ");
5823
0
    return FALSE;
5824
0
  }
5825
5826
  /* ensure the device ID is already setup */
5827
0
  if (!fu_device_ensure_id(self, error)) {
5828
0
    g_prefix_error(error, "failed to ensure ID: ");
5829
0
    return FALSE;
5830
0
  }
5831
5832
  /* subclassed */
5833
0
  if (device_class->open != NULL) {
5834
0
    if (fu_device_has_private_flag(self, FU_DEVICE_PRIVATE_FLAG_RETRY_OPEN)) {
5835
0
      if (!fu_device_retry_full(self,
5836
0
              fu_device_open_cb,
5837
0
              FU_DEVICE_RETRY_OPEN_COUNT,
5838
0
              FU_DEVICE_RETRY_OPEN_DELAY,
5839
0
              NULL,
5840
0
              error)) {
5841
0
        g_prefix_error(error, "failed to retry subclass open: ");
5842
0
        return FALSE;
5843
0
      }
5844
0
    } else {
5845
0
      if (!device_class->open(self, error)) {
5846
0
        g_prefix_error(error, "failed to subclass open: ");
5847
0
        return FALSE;
5848
0
      }
5849
0
    }
5850
0
  }
5851
5852
  /* setup */
5853
0
  if (!fu_device_setup(self, error)) {
5854
0
    g_prefix_error(error, "failed to setup: ");
5855
0
    return FALSE;
5856
0
  }
5857
5858
  /* ensure the device ID is still valid */
5859
0
  if (!fu_device_ensure_id(self, error)) {
5860
0
    g_prefix_error(error, "failed to ensure ID: ");
5861
0
    return FALSE;
5862
0
  }
5863
5864
  /* success */
5865
0
  fu_device_add_private_flag(self, FU_DEVICE_PRIVATE_FLAG_IS_OPEN);
5866
0
  return TRUE;
5867
0
}
5868
5869
/**
5870
 * fu_device_open:
5871
 * @self: a #FuDevice
5872
 * @error: (nullable): optional return location for an error
5873
 *
5874
 * Opens a device, optionally running a object-specific vfunc.
5875
 *
5876
 * Plugins can call fu_device_open() multiple times without calling
5877
 * fu_device_close(), but only the first call will actually invoke the vfunc.
5878
 *
5879
 * It is expected that plugins issue the same number of fu_device_open() and
5880
 * fu_device_close() methods when using a specific @self.
5881
 *
5882
 * If the `->probe()`, `->open()` and `->setup()` actions all complete
5883
 * successfully the internal device flag %FU_DEVICE_PRIVATE_FLAG_IS_OPEN will
5884
 * be set.
5885
 *
5886
 * NOTE: It is important to still call fu_device_close() even if this function
5887
 * fails as the device may still be partially initialized.
5888
 *
5889
 * Returns: %TRUE for success
5890
 *
5891
 * Since: 1.1.2
5892
 **/
5893
gboolean
5894
fu_device_open(FuDevice *self, GError **error)
5895
0
{
5896
0
  g_return_val_if_fail(FU_IS_DEVICE(self), FALSE);
5897
0
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
5898
5899
  /* skip */
5900
0
  if (fu_device_has_private_flag_quark(self, quarks[QUARK_IS_FAKE])) {
5901
0
    fu_device_add_private_flag(self, FU_DEVICE_PRIVATE_FLAG_IS_OPEN);
5902
0
    if (!fu_device_probe(self, error))
5903
0
      return FALSE;
5904
0
    if (!fu_device_setup(self, error))
5905
0
      return FALSE;
5906
0
    return fu_device_ensure_id(self, error);
5907
0
  }
5908
5909
  /* use parent */
5910
0
  if (fu_device_has_private_flag(self, FU_DEVICE_PRIVATE_FLAG_USE_PARENT_FOR_OPEN)) {
5911
0
    FuDevice *parent = fu_device_get_parent(self);
5912
0
    if (parent == NULL) {
5913
0
      g_set_error_literal(error,
5914
0
              FWUPD_ERROR,
5915
0
              FWUPD_ERROR_NOT_SUPPORTED,
5916
0
              "no parent device");
5917
0
      return FALSE;
5918
0
    }
5919
0
    return fu_device_open_internal(parent, error);
5920
0
  }
5921
0
  if (fu_device_has_private_flag(self, FU_DEVICE_PRIVATE_FLAG_USE_PROXY_FOR_OPEN)) {
5922
0
    FuDevice *proxy = fu_device_get_proxy(self);
5923
0
    if (proxy == NULL) {
5924
0
      g_set_error_literal(error,
5925
0
              FWUPD_ERROR,
5926
0
              FWUPD_ERROR_NOT_SUPPORTED,
5927
0
              "no proxy device");
5928
0
      return FALSE;
5929
0
    }
5930
0
    if (!fu_device_open_internal(proxy, error))
5931
0
      return FALSE;
5932
0
  }
5933
0
  return fu_device_open_internal(self, error);
5934
0
}
5935
5936
static gboolean
5937
fu_device_close_internal(FuDevice *self, GError **error)
5938
0
{
5939
0
  FuDeviceClass *device_class = FU_DEVICE_GET_CLASS(self);
5940
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
5941
5942
  /* not yet open */
5943
0
  if (priv->open_refcount == 0) {
5944
0
    g_set_error_literal(error,
5945
0
            FWUPD_ERROR,
5946
0
            FWUPD_ERROR_NOTHING_TO_DO,
5947
0
            "cannot close device, refcount already zero");
5948
0
    return FALSE;
5949
0
  }
5950
0
  if (!g_atomic_int_dec_and_test(&priv->open_refcount))
5951
0
    return TRUE;
5952
5953
  /* subclassed */
5954
0
  if (device_class->close != NULL) {
5955
0
    if (!device_class->close(self, error))
5956
0
      return FALSE;
5957
0
  }
5958
5959
  /* success */
5960
0
  fu_device_remove_private_flag(self, FU_DEVICE_PRIVATE_FLAG_IS_OPEN);
5961
0
  return TRUE;
5962
0
}
5963
5964
/**
5965
 * fu_device_close:
5966
 * @self: a #FuDevice
5967
 * @error: (nullable): optional return location for an error
5968
 *
5969
 * Closes a device, optionally running a object-specific vfunc.
5970
 *
5971
 * Plugins can call fu_device_close() multiple times without calling
5972
 * fu_device_open(), but only the last call will actually invoke the vfunc.
5973
 *
5974
 * It is expected that plugins issue the same number of fu_device_open() and
5975
 * fu_device_close() methods when using a specific @self.
5976
 *
5977
 * An error is returned if this method is called without having used the
5978
 * fu_device_open() method beforehand.
5979
 *
5980
 * If the close action completed successfully the internal device flag
5981
 * %FU_DEVICE_PRIVATE_FLAG_IS_OPEN will be cleared.
5982
 *
5983
 * Returns: %TRUE for success
5984
 *
5985
 * Since: 1.1.2
5986
 **/
5987
gboolean
5988
fu_device_close(FuDevice *self, GError **error)
5989
0
{
5990
0
  g_return_val_if_fail(FU_IS_DEVICE(self), FALSE);
5991
0
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
5992
5993
  /* skip */
5994
0
  if (fu_device_has_private_flag_quark(self, quarks[QUARK_IS_FAKE])) {
5995
0
    fu_device_remove_private_flag(self, FU_DEVICE_PRIVATE_FLAG_IS_OPEN);
5996
0
    return TRUE;
5997
0
  }
5998
5999
  /* close the device first in case the plugin needs to use the proxy or parent */
6000
0
  if (!fu_device_close_internal(self, error))
6001
0
    return FALSE;
6002
6003
  /* use parent */
6004
0
  if (fu_device_has_private_flag(self, FU_DEVICE_PRIVATE_FLAG_USE_PARENT_FOR_OPEN)) {
6005
0
    FuDevice *parent = fu_device_get_parent(self);
6006
0
    if (parent == NULL) {
6007
0
      g_set_error_literal(error,
6008
0
              FWUPD_ERROR,
6009
0
              FWUPD_ERROR_NOT_SUPPORTED,
6010
0
              "no parent device");
6011
0
      return FALSE;
6012
0
    }
6013
0
    return fu_device_close_internal(parent, error);
6014
0
  }
6015
0
  if (fu_device_has_private_flag(self, FU_DEVICE_PRIVATE_FLAG_USE_PROXY_FOR_OPEN)) {
6016
0
    FuDevice *proxy = fu_device_get_proxy(self);
6017
0
    if (proxy == NULL) {
6018
0
      g_set_error_literal(error,
6019
0
              FWUPD_ERROR,
6020
0
              FWUPD_ERROR_NOT_SUPPORTED,
6021
0
              "no proxy device");
6022
0
      return FALSE;
6023
0
    }
6024
0
    if (!fu_device_close_internal(proxy, error))
6025
0
      return FALSE;
6026
0
  }
6027
6028
  /* success */
6029
0
  return TRUE;
6030
0
}
6031
6032
/**
6033
 * fu_device_probe:
6034
 * @self: a #FuDevice
6035
 * @error: (nullable): optional return location for an error
6036
 *
6037
 * Probes a device, setting parameters on the object that does not need
6038
 * the device open or the interface claimed.
6039
 * If the device is not compatible then an error should be returned.
6040
 *
6041
 * Returns: %TRUE for success
6042
 *
6043
 * Since: 1.1.2
6044
 **/
6045
gboolean
6046
fu_device_probe(FuDevice *self, GError **error)
6047
0
{
6048
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
6049
0
  FuDeviceClass *device_class = FU_DEVICE_GET_CLASS(self);
6050
6051
0
  g_return_val_if_fail(FU_IS_DEVICE(self), FALSE);
6052
0
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
6053
6054
  /* already done */
6055
0
  if (priv->done_probe)
6056
0
    return TRUE;
6057
6058
  /* device self-assigned */
6059
0
  if (fu_device_has_private_flag_quark(self, quarks[QUARK_NO_PROBE])) {
6060
0
    g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "not probing");
6061
0
    return FALSE;
6062
0
  }
6063
6064
  /* subclassed */
6065
0
  if (device_class->probe != NULL) {
6066
0
    if (!device_class->probe(self, error))
6067
0
      return FALSE;
6068
0
  }
6069
6070
  /* vfunc skipped device */
6071
0
  if (fu_device_has_private_flag_quark(self, quarks[QUARK_NO_PROBE])) {
6072
0
    g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "not probing");
6073
0
    return FALSE;
6074
0
  }
6075
6076
  /* success */
6077
0
  priv->done_probe = TRUE;
6078
0
  return TRUE;
6079
0
}
6080
6081
/**
6082
 * fu_device_probe_complete:
6083
 * @self: a #FuDevice
6084
 *
6085
 * Tell the device that all probing has finished. This allows it to release any resources that are
6086
 * only valid during coldplug or hotplug.
6087
 *
6088
 * Since: 1.8.12
6089
 **/
6090
void
6091
fu_device_probe_complete(FuDevice *self)
6092
0
{
6093
0
  FuDeviceClass *device_class = FU_DEVICE_GET_CLASS(self);
6094
6095
0
  g_return_if_fail(FU_IS_DEVICE(self));
6096
6097
0
  if (device_class->probe_complete != NULL)
6098
0
    device_class->probe_complete(self);
6099
0
}
6100
6101
/**
6102
 * fu_device_rescan:
6103
 * @self: a #FuDevice
6104
 * @error: (nullable): optional return location for an error
6105
 *
6106
 * Rescans a device, re-adding GUIDs or flags based on some hardware change.
6107
 *
6108
 * Returns: %TRUE for success
6109
 *
6110
 * Since: 1.3.1
6111
 **/
6112
gboolean
6113
fu_device_rescan(FuDevice *self, GError **error)
6114
0
{
6115
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
6116
0
  FuDeviceClass *device_class = FU_DEVICE_GET_CLASS(self);
6117
6118
0
  g_return_val_if_fail(FU_IS_DEVICE(self), FALSE);
6119
0
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
6120
6121
  /* remove all GUIDs */
6122
0
  if (priv->instance_ids != NULL)
6123
0
    g_ptr_array_set_size(priv->instance_ids, 0);
6124
0
  g_ptr_array_set_size(fu_device_get_instance_ids(self), 0);
6125
0
  g_ptr_array_set_size(fu_device_get_guids(self), 0);
6126
6127
  /* subclassed */
6128
0
  if (device_class->rescan != NULL) {
6129
0
    if (!device_class->rescan(self, error)) {
6130
0
      fu_device_convert_instance_ids(self);
6131
0
      return FALSE;
6132
0
    }
6133
0
  }
6134
6135
0
  fu_device_convert_instance_ids(self);
6136
0
  return TRUE;
6137
0
}
6138
6139
/**
6140
 * fu_device_set_progress:
6141
 * @self: a #FuDevice
6142
 * @progress: a #FuProgress
6143
 *
6144
 * Sets steps on the progress object used to write firmware.
6145
 *
6146
 * Since: 1.7.0
6147
 **/
6148
void
6149
fu_device_set_progress(FuDevice *self, FuProgress *progress)
6150
0
{
6151
0
  FuDeviceClass *device_class = FU_DEVICE_GET_CLASS(self);
6152
6153
0
  g_return_if_fail(FU_IS_DEVICE(self));
6154
0
  g_return_if_fail(FU_IS_PROGRESS(progress));
6155
6156
  /* subclassed */
6157
0
  if (device_class->set_progress == NULL)
6158
0
    return;
6159
0
  device_class->set_progress(self, progress);
6160
0
}
6161
6162
/**
6163
 * fu_device_convert_instance_ids:
6164
 * @self: a #FuDevice
6165
 *
6166
 * Converts all the Device instance IDs added using fu_device_add_instance_id()
6167
 * into actual GUIDs, **unless** %FU_DEVICE_PRIVATE_FLAG_NO_AUTO_INSTANCE_IDS has
6168
 * been set.
6169
 *
6170
 * Plugins will only need to need to call this manually when adding child
6171
 * devices, as fu_device_setup() automatically calls this after the
6172
 * fu_device_probe() and fu_device_setup() virtual functions have been run.
6173
 *
6174
 * Since: 1.2.5
6175
 **/
6176
void
6177
fu_device_convert_instance_ids(FuDevice *self)
6178
0
{
6179
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
6180
0
  gboolean no_generic_guids;
6181
6182
0
  g_return_if_fail(FU_IS_DEVICE(self));
6183
6184
  /* already set */
6185
0
  if (fu_device_get_guids(self)->len > 0)
6186
0
    return;
6187
6188
  /* convert the FuDevice IDs to FwupdDevice IDs */
6189
0
  no_generic_guids = fu_device_has_private_flag_quark(self, quarks[QUARK_NO_GENERIC_GUIDS]);
6190
0
  if (priv->instance_ids != NULL) {
6191
0
    for (guint i = 0; i < priv->instance_ids->len; i++) {
6192
0
      FuDeviceInstanceIdItem *item = g_ptr_array_index(priv->instance_ids, i);
6193
0
      if ((item->flags & FU_DEVICE_INSTANCE_FLAG_VISIBLE) == 0)
6194
0
        continue;
6195
0
      if ((item->flags & FU_DEVICE_INSTANCE_FLAG_GENERIC) > 0 && no_generic_guids)
6196
0
        continue;
6197
0
      if (item->instance_id != NULL)
6198
0
        fwupd_device_add_instance_id(FWUPD_DEVICE(self), item->instance_id);
6199
0
      fwupd_device_add_guid(FWUPD_DEVICE(self), item->guid);
6200
0
    }
6201
0
  }
6202
6203
  /* OEM specific hardware */
6204
0
  if (fu_device_has_private_flag(self, FU_DEVICE_PRIVATE_FLAG_NO_AUTO_INSTANCE_IDS))
6205
0
    return;
6206
0
}
6207
6208
/**
6209
 * fu_device_setup:
6210
 * @self: a #FuDevice
6211
 * @error: (nullable): optional return location for an error
6212
 *
6213
 * Sets up a device, setting parameters on the object that requires
6214
 * the device to be open and have the interface claimed.
6215
 * If the device is not compatible then an error should be returned.
6216
 *
6217
 * Returns: %TRUE for success
6218
 *
6219
 * Since: 1.1.2
6220
 **/
6221
gboolean
6222
fu_device_setup(FuDevice *self, GError **error)
6223
0
{
6224
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
6225
0
  FuDeviceClass *device_class = FU_DEVICE_GET_CLASS(self);
6226
0
  GPtrArray *children;
6227
6228
0
  g_return_val_if_fail(FU_IS_DEVICE(self), FALSE);
6229
0
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
6230
6231
  /* skip */
6232
0
  if (fu_device_has_private_flag_quark(self, quarks[QUARK_IS_FAKE])) {
6233
0
    fu_device_convert_instance_ids(self);
6234
0
    return TRUE;
6235
0
  }
6236
6237
  /* should have already been called */
6238
0
  if (!fu_device_probe(self, error))
6239
0
    return FALSE;
6240
6241
  /* already done */
6242
0
  if (priv->done_setup)
6243
0
    return TRUE;
6244
6245
  /* subclassed */
6246
0
  if (device_class->setup != NULL) {
6247
0
    if (!device_class->setup(self, error))
6248
0
      return FALSE;
6249
0
  }
6250
6251
  /* vfunc skipped device */
6252
0
  if (fu_device_has_private_flag_quark(self, quarks[QUARK_NO_PROBE])) {
6253
0
    g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "not probing");
6254
0
    return FALSE;
6255
0
  }
6256
6257
  /* run setup on the children too (unless done already) */
6258
0
  children = fu_device_get_children(self);
6259
0
  for (guint i = 0; i < children->len; i++) {
6260
0
    FuDevice *child_tmp = g_ptr_array_index(children, i);
6261
0
    if (!fu_device_setup(child_tmp, error))
6262
0
      return FALSE;
6263
0
  }
6264
6265
  /* convert the instance IDs to GUIDs */
6266
0
  fu_device_convert_instance_ids(self);
6267
6268
  /* subclassed */
6269
0
  if (device_class->ready != NULL) {
6270
0
    if (!device_class->ready(self, error))
6271
0
      return FALSE;
6272
0
  }
6273
6274
0
  priv->done_setup = TRUE;
6275
0
  return TRUE;
6276
0
}
6277
6278
/**
6279
 * fu_device_activate:
6280
 * @self: a #FuDevice
6281
 * @progress: a #FuProgress
6282
 * @error: (nullable): optional return location for an error
6283
 *
6284
 * Activates up a device, which normally means the device switches to a new
6285
 * firmware version. This should only be called when data loss cannot occur.
6286
 *
6287
 * Returns: %TRUE for success
6288
 *
6289
 * Since: 1.2.6
6290
 **/
6291
gboolean
6292
fu_device_activate(FuDevice *self, FuProgress *progress, GError **error)
6293
0
{
6294
0
  FuDeviceClass *device_class = FU_DEVICE_GET_CLASS(self);
6295
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
6296
6297
0
  g_return_val_if_fail(FU_IS_DEVICE(self), FALSE);
6298
0
  g_return_val_if_fail(FU_IS_PROGRESS(progress), FALSE);
6299
0
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
6300
6301
  /* subclassed */
6302
0
  if (device_class->activate != NULL) {
6303
0
    g_set_object(&priv->progress, progress);
6304
0
    if (!device_class->activate(self, progress, error))
6305
0
      return FALSE;
6306
0
  }
6307
6308
0
  return TRUE;
6309
0
}
6310
6311
/**
6312
 * fu_device_probe_invalidate:
6313
 * @self: a #FuDevice
6314
 *
6315
 * Normally when calling fu_device_probe() multiple times it is only done once.
6316
 * Calling this method causes the next requests to fu_device_probe() and
6317
 * fu_device_setup() actually probe the hardware.
6318
 *
6319
 * This should be done in case the backing device has changed, for instance if
6320
 * a USB device has been replugged.
6321
 *
6322
 * Since: 1.1.2
6323
 **/
6324
void
6325
fu_device_probe_invalidate(FuDevice *self)
6326
0
{
6327
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
6328
0
  FuDeviceClass *device_class = FU_DEVICE_GET_CLASS(self);
6329
0
  g_return_if_fail(FU_IS_DEVICE(self));
6330
0
  priv->done_probe = FALSE;
6331
0
  priv->done_setup = FALSE;
6332
0
  if (device_class->invalidate != NULL)
6333
0
    device_class->invalidate(self);
6334
0
}
6335
6336
/**
6337
 * fu_device_report_metadata_pre:
6338
 * @self: a #FuDevice
6339
 *
6340
 * Collects metadata that would be useful for debugging a failed update report.
6341
 *
6342
 * Returns: (transfer full) (nullable): a #GHashTable, or %NULL if there is no data
6343
 *
6344
 * Since: 1.5.0
6345
 **/
6346
GHashTable *
6347
fu_device_report_metadata_pre(FuDevice *self)
6348
0
{
6349
0
  FuDeviceClass *device_class = FU_DEVICE_GET_CLASS(self);
6350
0
  g_autoptr(GHashTable) metadata = NULL;
6351
6352
0
  g_return_val_if_fail(FU_IS_DEVICE(self), NULL);
6353
6354
  /* not implemented */
6355
0
  if (device_class->report_metadata_pre == NULL)
6356
0
    return NULL;
6357
6358
  /* metadata for all devices */
6359
0
  metadata = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
6360
0
  device_class->report_metadata_pre(self, metadata);
6361
0
  return g_steal_pointer(&metadata);
6362
0
}
6363
6364
/**
6365
 * fu_device_report_metadata_post:
6366
 * @self: a #FuDevice
6367
 *
6368
 * Collects metadata that would be useful for debugging a failed update report.
6369
 *
6370
 * Returns: (transfer full) (nullable): a #GHashTable, or %NULL if there is no data
6371
 *
6372
 * Since: 1.5.0
6373
 **/
6374
GHashTable *
6375
fu_device_report_metadata_post(FuDevice *self)
6376
0
{
6377
0
  FuDeviceClass *device_class = FU_DEVICE_GET_CLASS(self);
6378
0
  g_autoptr(GHashTable) metadata = NULL;
6379
6380
0
  g_return_val_if_fail(FU_IS_DEVICE(self), NULL);
6381
6382
  /* not implemented */
6383
0
  if (device_class->report_metadata_post == NULL)
6384
0
    return NULL;
6385
6386
  /* metadata for all devices */
6387
0
  metadata = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
6388
0
  device_class->report_metadata_post(self, metadata);
6389
0
  return g_steal_pointer(&metadata);
6390
0
}
6391
6392
/**
6393
 * fu_device_add_security_attrs:
6394
 * @self: a #FuDevice
6395
 * @attrs: a security attribute
6396
 *
6397
 * Adds HSI security attributes.
6398
 *
6399
 * Since: 1.6.0
6400
 **/
6401
void
6402
fu_device_add_security_attrs(FuDevice *self, FuSecurityAttrs *attrs)
6403
0
{
6404
0
  FuDeviceClass *device_class = FU_DEVICE_GET_CLASS(self);
6405
6406
0
  g_return_if_fail(FU_IS_DEVICE(self));
6407
6408
  /* optional */
6409
0
  if (device_class->add_security_attrs != NULL)
6410
0
    return device_class->add_security_attrs(self, attrs);
6411
0
}
6412
6413
/**
6414
 * fu_device_bind_driver:
6415
 * @self: a #FuDevice
6416
 * @subsystem: a subsystem string, e.g. `pci`
6417
 * @driver: a kernel module name, e.g. `tg3`
6418
 * @error: (nullable): optional return location for an error
6419
 *
6420
 * Binds a driver to the device, which normally means the kernel driver takes
6421
 * control of the hardware.
6422
 *
6423
 * Returns: %TRUE if driver was bound.
6424
 *
6425
 * Since: 1.5.0
6426
 **/
6427
gboolean
6428
fu_device_bind_driver(FuDevice *self, const gchar *subsystem, const gchar *driver, GError **error)
6429
0
{
6430
0
  FuDeviceClass *device_class = FU_DEVICE_GET_CLASS(self);
6431
6432
0
  g_return_val_if_fail(FU_IS_DEVICE(self), FALSE);
6433
0
  g_return_val_if_fail(subsystem != NULL, FALSE);
6434
0
  g_return_val_if_fail(driver != NULL, FALSE);
6435
0
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
6436
6437
  /* not implemented */
6438
0
  if (device_class->bind_driver == NULL) {
6439
0
    g_set_error_literal(error,
6440
0
            FWUPD_ERROR,
6441
0
            FWUPD_ERROR_NOT_SUPPORTED,
6442
0
            "binding drivers is not supported by device");
6443
0
    return FALSE;
6444
0
  }
6445
6446
  /* subclass */
6447
0
  return device_class->bind_driver(self, subsystem, driver, error);
6448
0
}
6449
6450
/**
6451
 * fu_device_unbind_driver:
6452
 * @self: a #FuDevice
6453
 * @error: (nullable): optional return location for an error
6454
 *
6455
 * Unbinds the driver from the device, which normally means the kernel releases
6456
 * the hardware so it can be used from userspace.
6457
 *
6458
 * If there is no driver bound then this function will return with success
6459
 * without actually doing anything.
6460
 *
6461
 * Returns: %TRUE if driver was unbound.
6462
 *
6463
 * Since: 1.5.0
6464
 **/
6465
gboolean
6466
fu_device_unbind_driver(FuDevice *self, GError **error)
6467
0
{
6468
0
  FuDeviceClass *device_class = FU_DEVICE_GET_CLASS(self);
6469
6470
0
  g_return_val_if_fail(FU_IS_DEVICE(self), FALSE);
6471
0
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
6472
6473
  /* not implemented */
6474
0
  if (device_class->unbind_driver == NULL) {
6475
0
    g_set_error_literal(error,
6476
0
            FWUPD_ERROR,
6477
0
            FWUPD_ERROR_NOT_SUPPORTED,
6478
0
            "unbinding drivers is not supported by device");
6479
0
    return FALSE;
6480
0
  }
6481
6482
  /* subclass */
6483
0
  return device_class->unbind_driver(self, error);
6484
0
}
6485
6486
/**
6487
 * fu_device_get_instance_str:
6488
 * @self: a #FuDevice
6489
 * @key: (not nullable): a key, e.g. `REV`
6490
 *
6491
 * Looks up an instance ID by a key.
6492
 *
6493
 * Returns: (nullable) (transfer none): The instance key, or %NULL.
6494
 *
6495
 * Since: 1.8.15
6496
 **/
6497
const gchar *
6498
fu_device_get_instance_str(FuDevice *self, const gchar *key)
6499
0
{
6500
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
6501
0
  g_return_val_if_fail(FU_IS_DEVICE(self), NULL);
6502
0
  g_return_val_if_fail(key != NULL, NULL);
6503
0
  if (priv->instance_hash == NULL)
6504
0
    return NULL;
6505
0
  return g_hash_table_lookup(priv->instance_hash, key);
6506
0
}
6507
6508
/**
6509
 * fu_device_build_vendor_id:
6510
 * @self: a #FuDevice
6511
 * @prefix: (not nullable): a prefix string, e.g. `USB`
6512
 * @value: (nullable): a value, e.g. `0x1234`
6513
 *
6514
 * Builds a device vendor ID, if @value is not %NULL.
6515
 *
6516
 * Since: 2.0.0
6517
 **/
6518
void
6519
fu_device_build_vendor_id(FuDevice *self, const gchar *prefix, const gchar *value)
6520
0
{
6521
0
  g_autofree gchar *vendor_id = NULL;
6522
6523
0
  g_return_if_fail(FU_IS_DEVICE(self));
6524
0
  g_return_if_fail(prefix != NULL);
6525
6526
0
  if (value == NULL)
6527
0
    return;
6528
0
  vendor_id = g_strdup_printf("%s:%s", prefix, value);
6529
0
  fwupd_device_add_vendor_id(FWUPD_DEVICE(self), vendor_id);
6530
0
}
6531
6532
/**
6533
 * fu_device_build_vendor_id_u16:
6534
 * @self: a #FuDevice
6535
 * @prefix: (not nullable): a prefix string, e.g. `USB`
6536
 * @value: a value, e.g. 0x1234
6537
 *
6538
 * Builds a device vendor ID, if @value is not 0.
6539
 *
6540
 * Since: 2.0.0
6541
 **/
6542
void
6543
fu_device_build_vendor_id_u16(FuDevice *self, const gchar *prefix, guint16 value)
6544
0
{
6545
0
  g_autofree gchar *vendor_id = NULL;
6546
6547
0
  g_return_if_fail(FU_IS_DEVICE(self));
6548
0
  g_return_if_fail(prefix != NULL);
6549
6550
0
  if (value == 0x0)
6551
0
    return;
6552
0
  vendor_id = g_strdup_printf("%s:0x%04X", prefix, value);
6553
0
  fwupd_device_add_vendor_id(FWUPD_DEVICE(self), vendor_id);
6554
0
}
6555
6556
static void
6557
fu_device_incorporate_instance_ids(FuDevice *self, FuDevice *donor)
6558
0
{
6559
0
  FuDevicePrivate *priv_donor = GET_PRIVATE(donor);
6560
0
  gboolean no_generic_guids;
6561
6562
0
  if (priv_donor->instance_ids == NULL)
6563
0
    return;
6564
0
  no_generic_guids = fu_device_has_private_flag_quark(self, quarks[QUARK_NO_GENERIC_GUIDS]);
6565
0
  for (guint i = 0; i < priv_donor->instance_ids->len; i++) {
6566
0
    FuDeviceInstanceIdItem *item = g_ptr_array_index(priv_donor->instance_ids, i);
6567
0
    if ((item->flags & FU_DEVICE_INSTANCE_FLAG_GENERIC) > 0 && no_generic_guids) {
6568
0
      continue;
6569
0
    }
6570
0
    if (item->instance_id != NULL)
6571
0
      fu_device_add_instance_id_full(self, item->instance_id, item->flags);
6572
0
    else
6573
0
      fu_device_add_instance_id_full(self, item->guid, item->flags);
6574
0
  }
6575
0
}
6576
6577
/**
6578
 * fu_device_incorporate:
6579
 * @self: a #FuDevice
6580
 * @donor: Another #FuDevice
6581
 * @flag: Some #FuDeviceIncorporateFlags, e.g. %FU_DEVICE_INCORPORATE_FLAG_PHYSICAL_ID
6582
 *
6583
 * Copy some properties from the donor object if they have not already been set.
6584
 *
6585
 * Since: 2.0.0
6586
 **/
6587
void
6588
fu_device_incorporate(FuDevice *self, FuDevice *donor, FuDeviceIncorporateFlags flag)
6589
0
{
6590
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
6591
0
  FuDevicePrivate *priv_donor = GET_PRIVATE(donor);
6592
6593
0
  g_return_if_fail(FU_IS_DEVICE(self));
6594
0
  g_return_if_fail(FU_IS_DEVICE(donor));
6595
6596
  /* do these unconditionally */
6597
0
  if (priv->ctx == NULL && priv_donor->ctx != NULL)
6598
0
    fu_device_set_context(self, priv_donor->ctx);
6599
0
  if (priv->backend == NULL && priv_donor->backend != NULL)
6600
0
    fu_device_set_backend(self, priv_donor->backend);
6601
6602
  /* bitflags */
6603
0
  if (flag & FU_DEVICE_INCORPORATE_FLAG_BASECLASS) {
6604
0
    fwupd_device_incorporate(FWUPD_DEVICE(self), FWUPD_DEVICE(donor));
6605
0
    if (fu_device_get_id(self) != NULL)
6606
0
      priv->device_id_valid = TRUE;
6607
    /* remove the baseclass-added serial number and GUIDs if set */
6608
0
    if (fu_device_has_private_flag_quark(self, quarks[QUARK_NO_SERIAL_NUMBER]))
6609
0
      fwupd_device_set_serial(FWUPD_DEVICE(self), NULL);
6610
0
  }
6611
0
  if (flag & FU_DEVICE_INCORPORATE_FLAG_VENDOR) {
6612
0
    if (fu_device_get_vendor(self) == NULL && fu_device_get_vendor(donor) != NULL)
6613
0
      fu_device_set_vendor(self, fu_device_get_vendor(donor));
6614
0
  }
6615
0
  if (flag & FU_DEVICE_INCORPORATE_FLAG_PHYSICAL_ID) {
6616
0
    if (priv->physical_id == NULL && priv_donor->physical_id != NULL)
6617
0
      fu_device_set_physical_id(self, priv_donor->physical_id);
6618
0
  }
6619
0
  if (flag & FU_DEVICE_INCORPORATE_FLAG_LOGICAL_ID) {
6620
0
    if (priv->logical_id == NULL && priv_donor->logical_id != NULL)
6621
0
      fu_device_set_logical_id(self, priv_donor->logical_id);
6622
0
  }
6623
0
  if (flag & FU_DEVICE_INCORPORATE_FLAG_BACKEND_ID) {
6624
0
    if (priv->backend_id == NULL && priv_donor->backend_id != NULL)
6625
0
      fu_device_set_backend_id(self, priv_donor->backend_id);
6626
0
  }
6627
0
  if (flag & FU_DEVICE_INCORPORATE_FLAG_VID) {
6628
0
    if (priv->vid == 0x0 && priv_donor->vid != 0x0)
6629
0
      fu_device_set_vid(self, priv_donor->vid);
6630
0
  }
6631
0
  if (flag & FU_DEVICE_INCORPORATE_FLAG_PID) {
6632
0
    if (priv->pid == 0x0 && priv_donor->pid != 0x0)
6633
0
      fu_device_set_pid(self, priv_donor->pid);
6634
0
  }
6635
0
  if (flag & FU_DEVICE_INCORPORATE_FLAG_VENDOR_IDS) {
6636
0
    GPtrArray *vendor_ids = fu_device_get_vendor_ids(donor);
6637
0
    for (guint i = 0; i < vendor_ids->len; i++) {
6638
0
      const gchar *vendor_id = g_ptr_array_index(vendor_ids, i);
6639
0
      fu_device_add_vendor_id(self, vendor_id);
6640
0
    }
6641
0
  }
6642
0
  if (flag & FU_DEVICE_INCORPORATE_FLAG_REMOVE_DELAY) {
6643
0
    if (priv->remove_delay == 0 && priv_donor->remove_delay != 0)
6644
0
      fu_device_set_remove_delay(self, priv_donor->remove_delay);
6645
0
  }
6646
0
  if (flag & FU_DEVICE_INCORPORATE_FLAG_ACQUIESCE_DELAY) {
6647
0
    if (priv->acquiesce_delay == 0 && priv_donor->acquiesce_delay != 0)
6648
0
      fu_device_set_acquiesce_delay(self, priv_donor->acquiesce_delay);
6649
0
  }
6650
0
  if (flag & FU_DEVICE_INCORPORATE_FLAG_ICONS) {
6651
0
    if (fu_device_get_icons(self)->len == 0) {
6652
0
      GPtrArray *icons = fu_device_get_icons(donor);
6653
0
      for (guint i = 0; i < icons->len; i++) {
6654
0
        const gchar *icon_name = g_ptr_array_index(icons, i);
6655
0
        fu_device_add_icon(self, icon_name);
6656
0
      }
6657
0
    }
6658
0
  }
6659
0
  if (flag & FU_DEVICE_INCORPORATE_FLAG_EVENTS) {
6660
0
    if (priv_donor->events != NULL && donor != priv->proxy) {
6661
0
      for (guint i = 0; i < priv_donor->events->len; i++) {
6662
0
        FuDeviceEvent *event = g_ptr_array_index(priv_donor->events, i);
6663
0
        fu_device_add_event(self, event);
6664
0
      }
6665
0
    }
6666
0
  }
6667
0
  if (flag & FU_DEVICE_INCORPORATE_FLAG_UPDATE_ERROR) {
6668
0
    if (fu_device_get_update_error(self) == NULL &&
6669
0
        fu_device_get_update_error(donor) != NULL) {
6670
0
      fu_device_set_update_error(self, fu_device_get_update_error(donor));
6671
0
    }
6672
0
  }
6673
0
  if (flag & FU_DEVICE_INCORPORATE_FLAG_UPDATE_STATE) {
6674
0
    if (fu_device_get_update_state(self) == FWUPD_UPDATE_STATE_UNKNOWN &&
6675
0
        fu_device_get_update_state(donor) != FWUPD_UPDATE_STATE_UNKNOWN) {
6676
0
      fu_device_set_update_state(self, fu_device_get_update_state(donor));
6677
0
    }
6678
0
  }
6679
0
  if (flag & FU_DEVICE_INCORPORATE_FLAG_SUPERCLASS) {
6680
0
    gpointer device_class_incorporate_last = NULL;
6681
0
    g_autoptr(GPtrArray) class_parents =
6682
0
        fu_device_get_common_class_parents(self, donor);
6683
6684
    /* run every unique ->incorporate() in each subclass */
6685
0
    for (guint i = 0; i < class_parents->len; i++) {
6686
0
      FuDeviceClass *device_class = g_ptr_array_index(class_parents, i);
6687
0
      if (device_class->incorporate != NULL &&
6688
0
          device_class->incorporate != device_class_incorporate_last) {
6689
0
        device_class->incorporate(self, donor);
6690
0
        device_class_incorporate_last = device_class->incorporate;
6691
0
      }
6692
0
    }
6693
0
  }
6694
0
  if (flag & FU_DEVICE_INCORPORATE_FLAG_UPDATE_MESSAGE) {
6695
0
    if (priv->update_message == NULL && priv_donor->update_message != NULL)
6696
0
      fu_device_set_update_message(self, priv_donor->update_message);
6697
0
  }
6698
0
  if (flag & FU_DEVICE_INCORPORATE_FLAG_UPDATE_IMAGE) {
6699
0
    if (priv->update_image == NULL && priv_donor->update_image != NULL)
6700
0
      fu_device_set_update_image(self, priv_donor->update_image);
6701
0
  }
6702
0
  if (flag & FU_DEVICE_INCORPORATE_FLAG_INSTANCE_IDS)
6703
0
    fu_device_incorporate_instance_ids(self, donor);
6704
0
  if (flag & FU_DEVICE_INCORPORATE_FLAG_GTYPE) {
6705
0
    if (priv->specialized_gtype == G_TYPE_INVALID &&
6706
0
        priv_donor->specialized_gtype != G_TYPE_INVALID) {
6707
0
      fu_device_set_specialized_gtype(self, priv_donor->specialized_gtype);
6708
0
    }
6709
0
  }
6710
0
  if (flag & FU_DEVICE_INCORPORATE_FLAG_POSSIBLE_PLUGINS) {
6711
0
    for (guint i = 0; i < priv_donor->possible_plugins->len; i++) {
6712
0
      const gchar *possible_plugin =
6713
0
          g_ptr_array_index(priv_donor->possible_plugins, i);
6714
0
      fu_device_add_possible_plugin(self, possible_plugin);
6715
0
    }
6716
0
  }
6717
0
  if (flag & FU_DEVICE_INCORPORATE_FLAG_INSTANCE_KEYS) {
6718
0
    if (priv_donor->instance_hash != NULL) {
6719
0
      GHashTableIter iter;
6720
0
      gpointer key, value;
6721
0
      g_hash_table_iter_init(&iter, priv_donor->instance_hash);
6722
0
      while (g_hash_table_iter_next(&iter, &key, &value)) {
6723
0
        if (fu_device_get_instance_str(self, key) == NULL)
6724
0
          fu_device_add_instance_str(self, key, value);
6725
0
      }
6726
0
    }
6727
0
  }
6728
6729
  /* everything else */
6730
0
  if (flag == FU_DEVICE_INCORPORATE_FLAG_ALL) {
6731
0
    GPtrArray *instance_ids = fu_device_get_instance_ids(donor);
6732
0
    GPtrArray *parent_physical_ids = fu_device_get_parent_physical_ids(donor);
6733
0
    GPtrArray *parent_backend_ids = fu_device_get_parent_backend_ids(donor);
6734
6735
    /* copy from donor FuDevice if has not already been set */
6736
0
    if (priv_donor->private_flags != NULL) {
6737
0
      fu_device_register_private_flags(self);
6738
0
      for (guint i = 0; i < priv_donor->private_flags->len; i++) {
6739
0
        GQuark flag_quark =
6740
0
            g_array_index(priv_donor->private_flags, GQuark, i);
6741
0
        if (fu_device_private_flags_has_registered_quark(self,
6742
0
                     flag_quark)) {
6743
0
          fu_device_add_private_flag_quark(self, flag_quark);
6744
0
        }
6745
0
      }
6746
0
    }
6747
0
    if (priv->created_usec == 0 && priv_donor->created_usec != 0)
6748
0
      fu_device_set_created_usec(self, priv_donor->created_usec);
6749
0
    if (priv->modified_usec == 0 && priv_donor->modified_usec != 0)
6750
0
      fu_device_set_modified_usec(self, priv_donor->modified_usec);
6751
0
    if (priv->equivalent_id == NULL && fu_device_get_equivalent_id(donor) != NULL)
6752
0
      fu_device_set_equivalent_id(self, fu_device_get_equivalent_id(donor));
6753
0
    if (priv->fwupd_version == NULL && priv_donor->fwupd_version != NULL)
6754
0
      fu_device_set_fwupd_version(self, priv_donor->fwupd_version);
6755
0
    if (priv_donor->required_free > 0)
6756
0
      fu_device_set_required_free(self, priv_donor->required_free);
6757
0
    if (priv->update_request_id == NULL && priv_donor->update_request_id != NULL)
6758
0
      fu_device_set_update_request_id(self, priv_donor->update_request_id);
6759
0
    if (fu_device_has_private_flag_quark(self, quarks[QUARK_REFCOUNTED_PROXY]) &&
6760
0
        fu_device_has_private_flag_quark(donor, quarks[QUARK_REFCOUNTED_PROXY])) {
6761
0
      if (priv->proxy == NULL && priv_donor->proxy != NULL)
6762
0
        fu_device_set_proxy(self, priv_donor->proxy);
6763
0
    }
6764
0
    if (priv->proxy_guid == NULL && priv_donor->proxy_guid != NULL)
6765
0
      fu_device_set_proxy_guid(self, priv_donor->proxy_guid);
6766
0
    if (priv->custom_flags == NULL && priv_donor->custom_flags != NULL)
6767
0
      fu_device_set_custom_flags(self, priv_donor->custom_flags);
6768
0
    if (priv_donor->parent_guids != NULL) {
6769
0
      for (guint i = 0; i < priv_donor->parent_guids->len; i++) {
6770
0
        const gchar *guid = g_ptr_array_index(priv_donor->parent_guids, i);
6771
0
        fu_device_add_parent_guid(self, guid);
6772
0
      }
6773
0
    }
6774
0
    if (parent_physical_ids != NULL) {
6775
0
      for (guint i = 0; i < parent_physical_ids->len; i++) {
6776
0
        const gchar *tmp = g_ptr_array_index(parent_physical_ids, i);
6777
0
        fu_device_add_parent_physical_id(self, tmp);
6778
0
      }
6779
0
    }
6780
0
    if (parent_backend_ids != NULL) {
6781
0
      for (guint i = 0; i < parent_backend_ids->len; i++) {
6782
0
        const gchar *tmp = g_ptr_array_index(parent_backend_ids, i);
6783
0
        fu_device_add_parent_backend_id(self, tmp);
6784
0
      }
6785
0
    }
6786
0
    if (priv_donor->metadata != NULL) {
6787
0
      GHashTableIter iter;
6788
0
      gpointer key, value;
6789
0
      g_hash_table_iter_init(&iter, priv_donor->metadata);
6790
0
      while (g_hash_table_iter_next(&iter, &key, &value)) {
6791
0
        if (fu_device_get_metadata(self, key) == NULL)
6792
0
          fu_device_set_metadata(self, key, value);
6793
0
      }
6794
0
    }
6795
6796
    /* call the set_quirk_kv() vfunc for the superclassed object */
6797
0
    for (guint i = 0; i < instance_ids->len; i++) {
6798
0
      const gchar *instance_id = g_ptr_array_index(instance_ids, i);
6799
0
      g_autofree gchar *guid = fwupd_guid_hash_string(instance_id);
6800
0
      fu_device_add_guid_quirks(self, guid);
6801
0
    }
6802
0
  }
6803
0
}
6804
6805
/**
6806
 * fu_device_replace:
6807
 * @self: a #FuDevice
6808
 * @donor: the old #FuDevice
6809
 *
6810
 * Copy properties from the old (no-longer-connected) device to the new (connected) device.
6811
 *
6812
 * This is typcically called from the daemon device list and should not be called from plugin code.
6813
 *
6814
 * Since: 1.9.2
6815
 **/
6816
void
6817
fu_device_replace(FuDevice *self, FuDevice *donor)
6818
0
{
6819
0
  FuDeviceClass *device_class = FU_DEVICE_GET_CLASS(self);
6820
6821
0
  g_return_if_fail(FU_IS_DEVICE(self));
6822
0
  g_return_if_fail(FU_IS_DEVICE(donor));
6823
6824
  /* optional subclass */
6825
0
  if (device_class->replace != NULL)
6826
0
    device_class->replace(self, donor);
6827
0
}
6828
6829
/**
6830
 * fu_device_incorporate_flag:
6831
 * @self: a #FuDevice
6832
 * @donor: another device
6833
 * @flag: device flags
6834
 *
6835
 * Copy the value of a specific flag from the donor object.
6836
 *
6837
 * Since: 1.3.5
6838
 **/
6839
void
6840
fu_device_incorporate_flag(FuDevice *self, FuDevice *donor, FwupdDeviceFlags flag)
6841
0
{
6842
0
  if (fu_device_has_flag(donor, flag) && !fu_device_has_flag(self, flag)) {
6843
0
    g_debug("donor set %s", fwupd_device_flag_to_string(flag));
6844
0
    fu_device_add_flag(self, flag);
6845
0
  } else if (!fu_device_has_flag(donor, flag) && fu_device_has_flag(self, flag)) {
6846
0
    g_debug("donor unset %s", fwupd_device_flag_to_string(flag));
6847
0
    fu_device_remove_flag(self, flag);
6848
0
  }
6849
0
}
6850
6851
/**
6852
 * fu_device_incorporate_from_component: (skip):
6853
 * @self: a device
6854
 * @component: a Xmlb node
6855
 *
6856
 * Copy all properties from the donor AppStream component.
6857
 *
6858
 * Since: 1.2.4
6859
 **/
6860
void
6861
fu_device_incorporate_from_component(FuDevice *self, XbNode *component)
6862
0
{
6863
0
  const gchar *tmp;
6864
0
  g_return_if_fail(FU_IS_DEVICE(self));
6865
0
  g_return_if_fail(XB_IS_NODE(component));
6866
0
  tmp = xb_node_query_text(component, "custom/value[@key='LVFS::UpdateMessage']", NULL);
6867
0
  if (tmp != NULL)
6868
0
    fu_device_set_update_message(self, tmp);
6869
0
  tmp = xb_node_query_text(component, "custom/value[@key='LVFS::UpdateImage']", NULL);
6870
0
  if (tmp != NULL)
6871
0
    fu_device_set_update_image(self, tmp);
6872
0
}
6873
6874
static void
6875
fu_device_ensure_from_component_name(FuDevice *self, XbNode *component)
6876
0
{
6877
0
  const gchar *name = NULL;
6878
6879
  /* copy 1:1 */
6880
0
  name = xb_node_query_text(component, "name", NULL);
6881
0
  if (name != NULL) {
6882
0
    fu_device_set_name(self, name);
6883
0
    fu_device_remove_private_flag(self, FU_DEVICE_PRIVATE_FLAG_MD_SET_NAME);
6884
0
  }
6885
0
}
6886
6887
static void
6888
fu_device_ensure_from_component_vendor(FuDevice *self, XbNode *component)
6889
0
{
6890
0
  const gchar *vendor = NULL;
6891
6892
  /* copy 1:1 */
6893
0
  vendor = xb_node_query_text(component, "developer_name", NULL);
6894
0
  if (vendor != NULL) {
6895
0
    fu_device_set_vendor(self, vendor);
6896
0
    fu_device_remove_private_flag(self, FU_DEVICE_PRIVATE_FLAG_MD_SET_VENDOR);
6897
0
  }
6898
0
}
6899
6900
static void
6901
fu_device_ensure_from_component_signed(FuDevice *self, XbNode *component)
6902
0
{
6903
0
  const gchar *value = NULL;
6904
6905
  /* already set, possibly by a quirk */
6906
0
  if (fu_device_has_flag(self, FWUPD_DEVICE_FLAG_SIGNED_PAYLOAD) ||
6907
0
      fu_device_has_flag(self, FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD))
6908
0
    return;
6909
6910
  /* copy 1:1 */
6911
0
  value = xb_node_query_text(component, "custom/value[@key='LVFS::DeviceIntegrity']", NULL);
6912
0
  if (value != NULL) {
6913
0
    if (g_strcmp0(value, "signed") == 0) {
6914
0
      fu_device_add_flag(self, FWUPD_DEVICE_FLAG_SIGNED_PAYLOAD);
6915
0
    } else if (g_strcmp0(value, "unsigned") == 0) {
6916
0
      fu_device_add_flag(self, FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD);
6917
0
    } else {
6918
0
      g_warning("payload value unexpected: %s, expected signed|unsigned", value);
6919
0
    }
6920
0
    fu_device_remove_private_flag(self, FU_DEVICE_PRIVATE_FLAG_MD_SET_VENDOR);
6921
0
  }
6922
0
}
6923
6924
static void
6925
fu_device_ensure_from_component_icon(FuDevice *self, XbNode *component)
6926
0
{
6927
0
  const gchar *icon = NULL;
6928
6929
  /* copy 1:1 */
6930
0
  icon = xb_node_query_text(component, "icon", NULL);
6931
0
  if (icon != NULL) {
6932
0
    fu_device_add_icon(self, icon);
6933
0
    fu_device_remove_private_flag(self, FU_DEVICE_PRIVATE_FLAG_MD_SET_ICON);
6934
0
  }
6935
0
}
6936
6937
static void
6938
fu_device_ensure_from_component_flags(FuDevice *self, XbNode *component)
6939
0
{
6940
0
  const gchar *tmp =
6941
0
      xb_node_query_text(component, "custom/value[@key='LVFS::DeviceFlags']", NULL);
6942
0
  if (tmp != NULL) {
6943
0
    g_auto(GStrv) hints = g_strsplit(tmp, ",", -1);
6944
0
    for (guint i = 0; hints[i] != NULL; i++)
6945
0
      fu_device_set_custom_flag(self, hints[i]);
6946
0
    fu_device_remove_private_flag(self, FU_DEVICE_PRIVATE_FLAG_MD_SET_FLAGS);
6947
0
  }
6948
0
}
6949
6950
static const gchar *
6951
fu_device_category_to_name(const gchar *cat)
6952
0
{
6953
0
  if (g_strcmp0(cat, "X-EmbeddedController") == 0)
6954
0
    return "Embedded Controller";
6955
0
  if (g_strcmp0(cat, "X-ManagementEngine") == 0)
6956
0
    return "Intel Management Engine";
6957
0
  if (g_strcmp0(cat, "X-CorporateManagementEngine") == 0)
6958
0
    return "Intel Management Engine";
6959
0
  if (g_strcmp0(cat, "X-ConsumerManagementEngine") == 0)
6960
0
    return "Intel Management Engine";
6961
0
  if (g_strcmp0(cat, "X-ThunderboltController") == 0)
6962
0
    return "Thunderbolt Controller";
6963
0
  if (g_strcmp0(cat, "X-PlatformSecurityProcessor") == 0)
6964
0
    return "Platform Security Processor";
6965
0
  if (g_strcmp0(cat, "X-CpuMicrocode") == 0)
6966
0
    return "CPU Microcode";
6967
0
  if (g_strcmp0(cat, "X-Battery") == 0)
6968
0
    return "Battery";
6969
0
  if (g_strcmp0(cat, "X-Camera") == 0)
6970
0
    return "Camera";
6971
0
  if (g_strcmp0(cat, "X-TPM") == 0)
6972
0
    return "TPM";
6973
0
  if (g_strcmp0(cat, "X-Touchpad") == 0)
6974
0
    return "Touchpad";
6975
0
  if (g_strcmp0(cat, "X-Mouse") == 0)
6976
0
    return "Mouse";
6977
0
  if (g_strcmp0(cat, "X-Keyboard") == 0)
6978
0
    return "Keyboard";
6979
0
  if (g_strcmp0(cat, "X-VideoDisplay") == 0)
6980
0
    return "Display";
6981
0
  if (g_strcmp0(cat, "X-BaseboardManagementController") == 0)
6982
0
    return "BMC";
6983
0
  if (g_strcmp0(cat, "X-UsbReceiver") == 0)
6984
0
    return "USB Receiver";
6985
0
  if (g_strcmp0(cat, "X-Gpu") == 0)
6986
0
    return "GPU";
6987
0
  if (g_strcmp0(cat, "X-Dock") == 0)
6988
0
    return "Dock";
6989
0
  if (g_strcmp0(cat, "X-UsbDock") == 0)
6990
0
    return "USB Dock";
6991
0
  if (g_strcmp0(cat, "X-FingerprintReader") == 0)
6992
0
    return "Fingerprint Reader";
6993
0
  if (g_strcmp0(cat, "X-GraphicsTablet") == 0)
6994
0
    return "Graphics Tablet";
6995
0
  if (g_strcmp0(cat, "X-InputController") == 0)
6996
0
    return "Input Controller";
6997
0
  if (g_strcmp0(cat, "X-Headphones") == 0)
6998
0
    return "Headphones";
6999
0
  if (g_strcmp0(cat, "X-Headset") == 0)
7000
0
    return "Headset";
7001
0
  return NULL;
7002
0
}
7003
7004
static void
7005
fu_device_ensure_from_component_name_category(FuDevice *self, XbNode *component)
7006
0
{
7007
0
  const gchar *name = NULL;
7008
0
  g_autoptr(GPtrArray) cats = NULL;
7009
7010
  /* get AppStream and safe-compat categories */
7011
0
  cats = xb_node_query(component, "categories/category|X-categories/category", 0, NULL);
7012
0
  if (cats == NULL)
7013
0
    return;
7014
0
  for (guint i = 0; i < cats->len; i++) {
7015
0
    XbNode *n = g_ptr_array_index(cats, i);
7016
0
    name = fu_device_category_to_name(xb_node_get_text(n));
7017
0
    if (name != NULL)
7018
0
      break;
7019
0
  }
7020
0
  if (name != NULL) {
7021
0
    fu_device_set_name(self, name);
7022
0
    fu_device_remove_private_flag(self, FU_DEVICE_PRIVATE_FLAG_MD_SET_NAME_CATEGORY);
7023
0
  }
7024
7025
  /* batteries updated using capsules should ignore the system power restriction */
7026
0
  if (g_strcmp0(fu_device_get_plugin(self), "uefi_capsule") == 0) {
7027
0
    gboolean is_battery = FALSE;
7028
0
    for (guint i = 0; i < cats->len; i++) {
7029
0
      XbNode *n = g_ptr_array_index(cats, i);
7030
0
      if (g_strcmp0(xb_node_get_text(n), "X-Battery") == 0) {
7031
0
        is_battery = TRUE;
7032
0
        break;
7033
0
      }
7034
0
    }
7035
0
    if (is_battery) {
7036
0
      g_info("ignoring system power for %s battery", fu_device_get_id(self));
7037
0
      fu_device_add_private_flag(self,
7038
0
               FU_DEVICE_PRIVATE_FLAG_IGNORE_SYSTEM_POWER);
7039
0
    }
7040
0
  }
7041
0
}
7042
7043
static void
7044
_g_ptr_array_reverse(GPtrArray *array)
7045
0
{
7046
0
  guint last_idx = array->len - 1;
7047
0
  for (guint i = 0; i < array->len / 2; i++) {
7048
0
    gpointer tmp = array->pdata[i];
7049
0
    array->pdata[i] = array->pdata[last_idx - i];
7050
0
    array->pdata[last_idx - i] = tmp;
7051
0
  }
7052
0
}
7053
7054
static void
7055
fu_device_ensure_from_component_verfmt(FuDevice *self, XbNode *component)
7056
0
{
7057
0
  FwupdVersionFormat verfmt = FWUPD_VERSION_FORMAT_UNKNOWN;
7058
0
  g_autoptr(GPtrArray) verfmts = NULL;
7059
7060
  /* get metadata */
7061
0
  verfmts = xb_node_query(component, "custom/value[@key='LVFS::VersionFormat']", 0, NULL);
7062
0
  if (verfmts == NULL)
7063
0
    return;
7064
0
  _g_ptr_array_reverse(verfmts);
7065
0
  for (guint i = 0; i < verfmts->len; i++) {
7066
0
    XbNode *value = g_ptr_array_index(verfmts, i);
7067
0
    verfmt = fwupd_version_format_from_string(xb_node_get_text(value));
7068
0
    if (verfmt != FWUPD_VERSION_FORMAT_UNKNOWN)
7069
0
      break;
7070
0
  }
7071
7072
  /* found and different to existing */
7073
0
  if (verfmt != FWUPD_VERSION_FORMAT_UNKNOWN &&
7074
0
      fu_device_get_version_format(self) != verfmt) {
7075
0
    fu_device_set_version_format(self, verfmt);
7076
0
    if (fu_device_get_version_raw(self) != 0x0) {
7077
0
      g_autofree gchar *version = NULL;
7078
0
      version = fu_version_from_uint32(fu_device_get_version_raw(self), verfmt);
7079
0
      fu_device_set_version(self, version);
7080
0
    }
7081
0
    if (fu_device_get_version_lowest_raw(self) != 0x0) {
7082
0
      g_autofree gchar *version = NULL;
7083
0
      version =
7084
0
          fu_version_from_uint32(fu_device_get_version_lowest_raw(self), verfmt);
7085
0
      fu_device_set_version_lowest(self, version);
7086
0
    }
7087
0
    if (fu_device_get_version_bootloader_raw(self) != 0x0) {
7088
0
      g_autofree gchar *version = NULL;
7089
0
      version = fu_version_from_uint32(fu_device_get_version_bootloader_raw(self),
7090
0
               verfmt);
7091
0
      fu_device_set_version_bootloader(self, version);
7092
0
    }
7093
0
  }
7094
7095
  /* do not try to do this again */
7096
0
  fu_device_remove_private_flag(self, FU_DEVICE_PRIVATE_FLAG_MD_SET_VERFMT);
7097
0
}
7098
7099
/**
7100
 * fu_device_ensure_from_component: (skip):
7101
 * @self: a device
7102
 * @component: a #XbNode
7103
 *
7104
 * Ensure all properties from the donor AppStream component as required.
7105
 *
7106
 * Since: 1.8.13
7107
 **/
7108
void
7109
fu_device_ensure_from_component(FuDevice *self, XbNode *component)
7110
0
{
7111
0
  g_return_if_fail(FU_IS_DEVICE(self));
7112
0
  g_return_if_fail(XB_IS_NODE(component));
7113
7114
  /* set the name */
7115
0
  if (fu_device_has_private_flag(self, FU_DEVICE_PRIVATE_FLAG_MD_SET_NAME))
7116
0
    fu_device_ensure_from_component_name(self, component);
7117
0
  if (fu_device_has_private_flag(self, FU_DEVICE_PRIVATE_FLAG_MD_SET_NAME_CATEGORY))
7118
0
    fu_device_ensure_from_component_name_category(self, component);
7119
0
  if (fu_device_has_private_flag(self, FU_DEVICE_PRIVATE_FLAG_MD_SET_ICON))
7120
0
    fu_device_ensure_from_component_icon(self, component);
7121
0
  if (fu_device_has_private_flag(self, FU_DEVICE_PRIVATE_FLAG_MD_SET_VENDOR))
7122
0
    fu_device_ensure_from_component_vendor(self, component);
7123
0
  if (fu_device_has_private_flag(self, FU_DEVICE_PRIVATE_FLAG_MD_SET_SIGNED))
7124
0
    fu_device_ensure_from_component_signed(self, component);
7125
0
  if (fu_device_has_private_flag(self, FU_DEVICE_PRIVATE_FLAG_MD_SET_VERFMT))
7126
0
    fu_device_ensure_from_component_verfmt(self, component);
7127
0
  if (fu_device_has_private_flag(self, FU_DEVICE_PRIVATE_FLAG_MD_SET_FLAGS))
7128
0
    fu_device_ensure_from_component_flags(self, component);
7129
0
}
7130
7131
/**
7132
 * fu_device_ensure_from_release:
7133
 * @self: a #FuDevice
7134
 * @rel: (not nullable): a #XbNode
7135
 *
7136
 * Ensure all properties from the donor AppStream release as required.
7137
 *
7138
 * Since: 2.0.5
7139
 **/
7140
void
7141
fu_device_ensure_from_release(FuDevice *self, XbNode *rel)
7142
0
{
7143
0
  g_return_if_fail(FU_IS_DEVICE(self));
7144
0
  g_return_if_fail(XB_IS_NODE(rel));
7145
7146
  /* set the required free */
7147
0
  if (fu_device_has_private_flag(self, FU_DEVICE_PRIVATE_FLAG_MD_SET_REQUIRED_FREE)) {
7148
0
    guint64 size;
7149
0
    size = xb_node_query_text_as_uint(rel,
7150
0
              "artifacts/artifact/size[@type='installed']",
7151
0
              NULL);
7152
0
    if (size != G_MAXUINT64) {
7153
0
      fu_device_set_required_free(self, size);
7154
0
      fu_device_remove_private_flag(self,
7155
0
                  FU_DEVICE_PRIVATE_FLAG_MD_SET_REQUIRED_FREE);
7156
0
    }
7157
0
  }
7158
7159
  /* optionally filter by device checksum */
7160
0
  if (fu_device_has_private_flag(self, FU_DEVICE_PRIVATE_FLAG_MD_ONLY_CHECKSUM)) {
7161
0
    gboolean valid = FALSE;
7162
0
    g_autoptr(GPtrArray) device_checksums = NULL;
7163
7164
0
    if (fu_device_get_checksums(self)->len == 0)
7165
0
      return;
7166
0
    device_checksums = xb_node_query(rel, "checksum[@target='device']", 0, NULL);
7167
0
    for (guint i = 0; device_checksums != NULL && i < device_checksums->len; i++) {
7168
0
      XbNode *device_checksum = g_ptr_array_index(device_checksums, i);
7169
0
      if (fu_device_has_checksum(self, xb_node_get_text(device_checksum))) {
7170
0
        valid = TRUE;
7171
0
        break;
7172
0
      }
7173
0
    }
7174
0
    if (!valid)
7175
0
      return;
7176
0
  }
7177
7178
  /* set the version */
7179
0
  if (fu_device_has_private_flag(self, FU_DEVICE_PRIVATE_FLAG_MD_SET_VERSION)) {
7180
0
    const gchar *version = xb_node_get_attr(rel, "version");
7181
0
    if (version != NULL) {
7182
0
      fu_device_set_version(self, version);
7183
0
      fu_device_remove_private_flag(self, FU_DEVICE_PRIVATE_FLAG_MD_SET_VERSION);
7184
0
    }
7185
0
  }
7186
0
}
7187
7188
/**
7189
 * fu_device_emit_request:
7190
 * @self: a device
7191
 * @request: a request
7192
 * @progress: (nullable): a #FuProgress
7193
 * @error: (nullable): optional return location for an error
7194
 *
7195
 * Emit a request from a plugin to the client.
7196
 *
7197
 * If the device is emulated then this request is ignored.
7198
 *
7199
 * Since: 1.9.8
7200
 **/
7201
gboolean
7202
fu_device_emit_request(FuDevice *self, FwupdRequest *request, FuProgress *progress, GError **error)
7203
0
{
7204
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
7205
7206
0
  g_return_val_if_fail(FU_IS_DEVICE(self), FALSE);
7207
0
  g_return_val_if_fail(FWUPD_IS_REQUEST(request), FALSE);
7208
0
  g_return_val_if_fail(progress == NULL || FU_IS_PROGRESS(progress), FALSE);
7209
0
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
7210
7211
0
#ifndef SUPPORTED_BUILD
7212
  /* nag the developer */
7213
0
  if (fwupd_request_has_flag(request, FWUPD_REQUEST_FLAG_ALLOW_GENERIC_MESSAGE) &&
7214
0
      !fu_device_has_request_flag(self, FWUPD_REQUEST_FLAG_ALLOW_GENERIC_MESSAGE)) {
7215
0
    g_set_error(error,
7216
0
          FWUPD_ERROR,
7217
0
          FWUPD_ERROR_NOT_SUPPORTED,
7218
0
          "request %s emitted but device %s [%s] does not set "
7219
0
          "FWUPD_REQUEST_FLAG_ALLOW_GENERIC_MESSAGE",
7220
0
          fwupd_request_get_id(request),
7221
0
          fu_device_get_id(self),
7222
0
          fu_device_get_plugin(self));
7223
0
    return FALSE;
7224
0
  }
7225
0
  if (!fwupd_request_has_flag(request, FWUPD_REQUEST_FLAG_ALLOW_GENERIC_MESSAGE) &&
7226
0
      !fu_device_has_request_flag(self, FWUPD_REQUEST_FLAG_NON_GENERIC_MESSAGE)) {
7227
0
    g_set_error(error,
7228
0
          FWUPD_ERROR,
7229
0
          FWUPD_ERROR_NOT_SUPPORTED,
7230
0
          "request %s is not a GENERIC_MESSAGE and device %s [%s] does not set "
7231
0
          "FWUPD_REQUEST_FLAG_NON_GENERIC_MESSAGE",
7232
0
          fwupd_request_get_id(request),
7233
0
          fu_device_get_id(self),
7234
0
          fu_device_get_plugin(self));
7235
0
    return FALSE;
7236
0
  }
7237
0
#endif
7238
7239
  /* sanity check */
7240
0
  if (fwupd_request_get_kind(request) == FWUPD_REQUEST_KIND_UNKNOWN) {
7241
0
    g_set_error_literal(error,
7242
0
            FWUPD_ERROR,
7243
0
            FWUPD_ERROR_NOT_SUPPORTED,
7244
0
            "a request must have an assigned kind");
7245
0
    return FALSE;
7246
0
  }
7247
0
  if (fwupd_request_get_id(request) == NULL) {
7248
0
    g_set_error_literal(error,
7249
0
            FWUPD_ERROR,
7250
0
            FWUPD_ERROR_NOT_SUPPORTED,
7251
0
            "a request must have an assigned ID");
7252
0
    return FALSE;
7253
0
  }
7254
0
  if (fwupd_request_get_kind(request) >= FWUPD_REQUEST_KIND_LAST) {
7255
0
    g_set_error_literal(error,
7256
0
            FWUPD_ERROR,
7257
0
            FWUPD_ERROR_NOT_SUPPORTED,
7258
0
            "invalid request kind");
7259
0
    return FALSE;
7260
0
  }
7261
7262
  /* already cancelled */
7263
0
  if (progress != NULL && fu_progress_has_flag(progress, FU_PROGRESS_FLAG_NO_SENDER)) {
7264
0
    g_set_error_literal(error,
7265
0
            FWUPD_ERROR,
7266
0
            FWUPD_ERROR_NOT_SUPPORTED,
7267
0
            "no sender, and so cannot process request");
7268
0
    return FALSE;
7269
0
  }
7270
7271
  /* ignore */
7272
0
  if (fu_device_has_flag(self, FWUPD_DEVICE_FLAG_EMULATED)) {
7273
0
    g_info("ignoring device %s request of %s as emulated",
7274
0
           fu_device_get_id(self),
7275
0
           fwupd_request_get_id(request));
7276
0
    return TRUE;
7277
0
  }
7278
7279
  /* ensure set */
7280
0
  fwupd_request_set_device_id(request, fu_device_get_id(self));
7281
7282
  /* for compatibility with older clients */
7283
0
  if (fwupd_request_get_kind(request) == FWUPD_REQUEST_KIND_POST) {
7284
0
    fu_device_set_update_message(self, fwupd_request_get_message(request));
7285
0
    fu_device_set_update_image(self, fwupd_request_get_image(request));
7286
0
  }
7287
7288
  /* proxy to the engine */
7289
0
  if (progress != NULL) {
7290
0
    fu_progress_set_status(progress, FWUPD_STATUS_WAITING_FOR_USER);
7291
0
  } else if (priv->progress != NULL) {
7292
0
    g_debug("using fallback progress");
7293
0
    fu_progress_set_status(priv->progress, FWUPD_STATUS_WAITING_FOR_USER);
7294
0
  } else {
7295
0
    g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "no progress");
7296
0
    return FALSE;
7297
0
  }
7298
0
  g_signal_emit(self, signals[SIGNAL_REQUEST], 0, request);
7299
0
  if (fwupd_request_get_kind(request) < FWUPD_REQUEST_KIND_LAST)
7300
0
    priv->request_cnts[fwupd_request_get_kind(request)]++;
7301
0
  return TRUE;
7302
0
}
7303
7304
static void
7305
fu_device_ensure_instance_hash(FuDevice *self)
7306
0
{
7307
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
7308
0
  if (priv->instance_hash != NULL)
7309
0
    return;
7310
0
  priv->instance_hash = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
7311
0
}
7312
7313
/**
7314
 * fu_device_add_instance_str:
7315
 * @self: a #FuDevice
7316
 * @key: (not nullable): string
7317
 * @value: (nullable): value
7318
 *
7319
 * Assign a value for the @key.
7320
 *
7321
 * Since: 1.7.7
7322
 **/
7323
void
7324
fu_device_add_instance_str(FuDevice *self, const gchar *key, const gchar *value)
7325
0
{
7326
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
7327
0
  g_return_if_fail(FU_IS_DEVICE(self));
7328
0
  g_return_if_fail(key != NULL);
7329
0
  fu_device_ensure_instance_hash(self);
7330
0
  g_hash_table_insert(priv->instance_hash, g_strdup(key), g_strdup(value));
7331
0
}
7332
7333
static gboolean
7334
fu_device_strsafe_instance_id_is_valid_char(gchar c)
7335
0
{
7336
0
  switch (c) {
7337
0
  case ' ':
7338
0
  case '_':
7339
0
  case '&':
7340
0
  case '/':
7341
0
  case '\\':
7342
0
  case '-':
7343
0
  case '(':
7344
0
  case ')':
7345
0
  case ',':
7346
0
    return FALSE;
7347
0
  default:
7348
0
    break;
7349
0
  }
7350
0
  return g_ascii_isprint(c);
7351
0
}
7352
7353
/* NOTE: we can't use fu_strsafe as this behavior is now effectively ABI */
7354
static gchar *
7355
fu_device_strsafe_instance_id(const gchar *str)
7356
0
{
7357
0
  g_autoptr(GString) tmp = g_string_new(NULL);
7358
0
  gboolean has_content = FALSE;
7359
7360
  /* sanity check */
7361
0
  if (str == NULL)
7362
0
    return NULL;
7363
7364
  /* use - to replace problematic chars -- but only once per section */
7365
0
  for (guint i = 0; str[i] != '\0'; i++) {
7366
0
    gchar c = str[i];
7367
0
    if (!fu_device_strsafe_instance_id_is_valid_char(c)) {
7368
0
      if (has_content) {
7369
0
        g_string_append_c(tmp, '-');
7370
0
        has_content = FALSE;
7371
0
      }
7372
0
    } else {
7373
0
      g_string_append_c(tmp, c);
7374
0
      has_content = TRUE;
7375
0
    }
7376
0
  }
7377
7378
  /* remove any trailing replacements */
7379
0
  if (tmp->len > 0 && tmp->str[tmp->len - 1] == '-')
7380
0
    g_string_truncate(tmp, tmp->len - 1);
7381
7382
  /* nothing left! */
7383
0
  if (tmp->len == 0)
7384
0
    return NULL;
7385
7386
  /* success */
7387
0
  return g_string_free(g_steal_pointer(&tmp), FALSE);
7388
0
}
7389
7390
/**
7391
 * fu_device_add_instance_strsafe:
7392
 * @self: a #FuDevice
7393
 * @key: (not nullable): string
7394
 * @value: (nullable): value
7395
 *
7396
 * Assign a sanitized value for the @key.
7397
 *
7398
 * Since: 1.7.7
7399
 **/
7400
void
7401
fu_device_add_instance_strsafe(FuDevice *self, const gchar *key, const gchar *value)
7402
0
{
7403
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
7404
0
  g_return_if_fail(FU_IS_DEVICE(self));
7405
0
  g_return_if_fail(key != NULL);
7406
0
  fu_device_ensure_instance_hash(self);
7407
0
  g_hash_table_insert(priv->instance_hash,
7408
0
          g_strdup(key),
7409
0
          fu_device_strsafe_instance_id(value));
7410
0
}
7411
7412
/**
7413
 * fu_device_add_instance_strup:
7414
 * @self: a #FuDevice
7415
 * @key: (not nullable): string
7416
 * @value: (nullable): value
7417
 *
7418
 * Assign a uppercase value for the @key.
7419
 *
7420
 * Since: 1.7.7
7421
 **/
7422
void
7423
fu_device_add_instance_strup(FuDevice *self, const gchar *key, const gchar *value)
7424
0
{
7425
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
7426
0
  g_return_if_fail(FU_IS_DEVICE(self));
7427
0
  g_return_if_fail(key != NULL);
7428
0
  fu_device_ensure_instance_hash(self);
7429
0
  g_hash_table_insert(priv->instance_hash,
7430
0
          g_strdup(key),
7431
0
          value != NULL ? g_utf8_strup(value, -1) : NULL);
7432
0
}
7433
7434
/**
7435
 * fu_device_add_instance_u4:
7436
 * @self: a #FuDevice
7437
 * @key: (not nullable): string
7438
 * @value: value
7439
 *
7440
 * Assign a value to the @key, which is padded as %1X.
7441
 *
7442
 * Since: 1.7.7
7443
 **/
7444
void
7445
fu_device_add_instance_u4(FuDevice *self, const gchar *key, guint8 value)
7446
0
{
7447
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
7448
0
  g_return_if_fail(FU_IS_DEVICE(self));
7449
0
  g_return_if_fail(key != NULL);
7450
0
  fu_device_ensure_instance_hash(self);
7451
0
  g_hash_table_insert(priv->instance_hash, g_strdup(key), g_strdup_printf("%01X", value));
7452
0
}
7453
7454
/**
7455
 * fu_device_add_instance_u8:
7456
 * @self: a #FuDevice
7457
 * @key: (not nullable): string
7458
 * @value: value
7459
 *
7460
 * Assign a value to the @key, which is padded as %2X.
7461
 *
7462
 * Since: 1.7.7
7463
 **/
7464
void
7465
fu_device_add_instance_u8(FuDevice *self, const gchar *key, guint8 value)
7466
0
{
7467
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
7468
0
  g_return_if_fail(FU_IS_DEVICE(self));
7469
0
  g_return_if_fail(key != NULL);
7470
0
  fu_device_ensure_instance_hash(self);
7471
0
  g_hash_table_insert(priv->instance_hash, g_strdup(key), g_strdup_printf("%02X", value));
7472
0
}
7473
7474
/**
7475
 * fu_device_add_instance_u16:
7476
 * @self: a #FuDevice
7477
 * @key: (not nullable): string
7478
 * @value: value
7479
 *
7480
 * Assign a value to the @key, which is padded as %4X.
7481
 *
7482
 * Since: 1.7.7
7483
 **/
7484
void
7485
fu_device_add_instance_u16(FuDevice *self, const gchar *key, guint16 value)
7486
0
{
7487
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
7488
0
  g_return_if_fail(FU_IS_DEVICE(self));
7489
0
  g_return_if_fail(key != NULL);
7490
0
  fu_device_ensure_instance_hash(self);
7491
0
  g_hash_table_insert(priv->instance_hash, g_strdup(key), g_strdup_printf("%04X", value));
7492
0
}
7493
7494
/**
7495
 * fu_device_add_instance_u32:
7496
 * @self: a #FuDevice
7497
 * @key: (not nullable): string
7498
 * @value: value
7499
 *
7500
 * Assign a value to the @key, which is padded as %8X.
7501
 *
7502
 * Since: 1.7.7
7503
 **/
7504
void
7505
fu_device_add_instance_u32(FuDevice *self, const gchar *key, guint32 value)
7506
0
{
7507
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
7508
0
  g_return_if_fail(FU_IS_DEVICE(self));
7509
0
  g_return_if_fail(key != NULL);
7510
0
  fu_device_ensure_instance_hash(self);
7511
0
  g_hash_table_insert(priv->instance_hash, g_strdup(key), g_strdup_printf("%08X", value));
7512
0
}
7513
7514
/**
7515
 * fu_device_build_instance_id_strv:
7516
 * @self: a #FuDevice
7517
 * @subsystem: (not nullable): subsystem, e.g. `NVME`
7518
 * @keys: (nullable): %NULL terminated array of string keys
7519
 * @error: (nullable): optional return location for an error
7520
 *
7521
 * Creates an instance ID from a prefix and an array of key values.
7522
 * If the key value cannot be found, the parent and then proxy is also queried.
7523
 *
7524
 * If any of the key values remain unset then no instance ID is added.
7525
 *
7526
 *  fu_device_add_instance_str(dev, "VID", "1234");
7527
 *  fu_device_add_instance_u16(dev, "PID", 5678);
7528
 *  const gchar *keys[] = {"VID", "PID", NULL};
7529
 *  if (!fu_device_build_instance_id_strv(dev, "NVME", keys, &error))
7530
 *    g_warning("failed to add ID: %s", error->message);
7531
 *
7532
 * Returns: %TRUE if the instance ID was added.
7533
 *
7534
 * Since: 2.0.14
7535
 **/
7536
gboolean
7537
fu_device_build_instance_id_strv(FuDevice *self,
7538
         const gchar *subsystem,
7539
         gchar **keys,
7540
         GError **error)
7541
0
{
7542
0
  FuDevice *parent = fu_device_get_parent(self);
7543
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
7544
0
  g_autoptr(GString) str = g_string_new(subsystem);
7545
7546
0
  g_return_val_if_fail(FU_IS_DEVICE(self), FALSE);
7547
0
  g_return_val_if_fail(subsystem != NULL, FALSE);
7548
7549
0
  for (guint i = 0; keys != NULL && keys[i] != NULL; i++) {
7550
0
    const gchar *key = keys[i];
7551
0
    const gchar *value;
7552
7553
0
    value = fu_device_get_instance_str(self, key);
7554
0
    if (value == NULL && parent != NULL)
7555
0
      value = fu_device_get_instance_str(parent, key);
7556
0
    if (value == NULL && priv->proxy != NULL)
7557
0
      value = fu_device_get_instance_str(priv->proxy, key);
7558
0
    if (value == NULL) {
7559
0
      g_set_error(error,
7560
0
            FWUPD_ERROR,
7561
0
            FWUPD_ERROR_INVALID_DATA,
7562
0
            "no value for %s",
7563
0
            key);
7564
0
      return FALSE;
7565
0
    }
7566
0
    g_string_append(str, i == 0 ? "\\" : "&");
7567
0
    g_string_append_printf(str, "%s_%s", key, value);
7568
0
  }
7569
7570
  /* success */
7571
0
  fu_device_add_instance_id(self, str->str);
7572
0
  return TRUE;
7573
0
}
7574
7575
/**
7576
 * fu_device_build_instance_id:
7577
 * @self: a #FuDevice
7578
 * @error: (nullable): optional return location for an error
7579
 * @subsystem: (not nullable): subsystem, e.g. `NVME`
7580
 * @...: pairs of string key values, ending with %NULL
7581
 *
7582
 * Creates an instance ID from a prefix and some key values.
7583
 * If the key value cannot be found, the parent and then proxy is also queried.
7584
 *
7585
 * If any of the key values remain unset then no instance ID is added.
7586
 *
7587
 *  fu_device_add_instance_str(dev, "VID", "1234");
7588
 *  fu_device_add_instance_u16(dev, "PID", 5678);
7589
 *  if (!fu_device_build_instance_id(dev, &error, "NVME", "VID", "PID", NULL))
7590
 *    g_warning("failed to add ID: %s", error->message);
7591
 *
7592
 * Returns: %TRUE if the instance ID was added.
7593
 *
7594
 * Since: 1.7.7
7595
 **/
7596
gboolean
7597
fu_device_build_instance_id(FuDevice *self, GError **error, const gchar *subsystem, ...)
7598
0
{
7599
0
  va_list args;
7600
0
  g_autoptr(GStrvBuilder) keys_builder = g_strv_builder_new();
7601
0
  g_auto(GStrv) keys = NULL;
7602
7603
0
  g_return_val_if_fail(FU_IS_DEVICE(self), FALSE);
7604
0
  g_return_val_if_fail(subsystem != NULL, FALSE);
7605
7606
  /* convert varargs to GStrv */
7607
0
  va_start(args, subsystem);
7608
0
  for (;;) {
7609
0
    const gchar *key = va_arg(args, const gchar *);
7610
0
    if (key == NULL)
7611
0
      break;
7612
0
    g_strv_builder_add(keys_builder, key);
7613
0
  }
7614
0
  va_end(args);
7615
0
  keys = g_strv_builder_end(keys_builder);
7616
7617
  /* call the strv version */
7618
0
  return fu_device_build_instance_id_strv(self, subsystem, keys, error);
7619
0
}
7620
7621
/**
7622
 * fu_device_build_instance_id_full:
7623
 * @self: a #FuDevice
7624
 * @flags: instance ID flags, e.g. %FU_DEVICE_INSTANCE_FLAG_QUIRKS
7625
 * @error: (nullable): optional return location for an error
7626
 * @subsystem: (not nullable): subsystem, e.g. `NVME`
7627
 * @...: pairs of string key values, ending with %NULL
7628
 *
7629
 * Creates an instance ID with specific flags from a prefix and some key values. If any of the key
7630
 * values are unset then no instance ID is added.
7631
 *
7632
 * Returns: %TRUE if the instance ID was added.
7633
 *
7634
 * Since: 1.9.8
7635
 **/
7636
gboolean
7637
fu_device_build_instance_id_full(FuDevice *self,
7638
         FuDeviceInstanceFlags flags,
7639
         GError **error,
7640
         const gchar *subsystem,
7641
         ...)
7642
0
{
7643
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
7644
0
  gboolean ret = TRUE;
7645
0
  va_list args;
7646
0
  g_autoptr(GString) str = g_string_new(subsystem);
7647
7648
0
  g_return_val_if_fail(FU_IS_DEVICE(self), FALSE);
7649
0
  g_return_val_if_fail(subsystem != NULL, FALSE);
7650
7651
0
  if (priv->instance_hash == NULL) {
7652
0
    g_set_error_literal(error,
7653
0
            FWUPD_ERROR,
7654
0
            FWUPD_ERROR_INVALID_DATA,
7655
0
            "no instance hash values defined");
7656
0
    return FALSE;
7657
0
  }
7658
0
  va_start(args, subsystem);
7659
0
  for (guint i = 0;; i++) {
7660
0
    const gchar *key = va_arg(args, const gchar *);
7661
0
    const gchar *value;
7662
0
    if (key == NULL)
7663
0
      break;
7664
0
    value = g_hash_table_lookup(priv->instance_hash, key);
7665
0
    if (value == NULL) {
7666
0
      g_set_error(error,
7667
0
            FWUPD_ERROR,
7668
0
            FWUPD_ERROR_INVALID_DATA,
7669
0
            "no value for %s",
7670
0
            key);
7671
0
      ret = FALSE;
7672
0
      break;
7673
0
    }
7674
0
    g_string_append(str, i == 0 ? "\\" : "&");
7675
0
    g_string_append_printf(str, "%s_%s", key, value);
7676
0
  }
7677
0
  va_end(args);
7678
7679
  /* we set an error above */
7680
0
  if (!ret)
7681
0
    return FALSE;
7682
7683
  /* success */
7684
0
  fu_device_add_instance_id_full(self, str->str, flags);
7685
0
  return TRUE;
7686
0
}
7687
7688
/**
7689
 * fu_device_security_attr_new:
7690
 * @self: a #FuDevice
7691
 * @appstream_id: (nullable): the AppStream component ID, e.g. `com.intel.BiosGuard`
7692
 *
7693
 * Creates a new #FwupdSecurityAttr for this specific device.
7694
 *
7695
 * Returns: (transfer full): a #FwupdSecurityAttr
7696
 *
7697
 * Since: 1.8.4
7698
 **/
7699
FwupdSecurityAttr *
7700
fu_device_security_attr_new(FuDevice *self, const gchar *appstream_id)
7701
0
{
7702
0
  FuDevicePrivate *priv = fu_device_get_instance_private(self);
7703
0
  g_autoptr(FwupdSecurityAttr) attr = NULL;
7704
7705
0
  g_return_val_if_fail(FU_IS_DEVICE(self), NULL);
7706
0
  g_return_val_if_fail(appstream_id != NULL, NULL);
7707
7708
0
  attr = fu_security_attr_new(priv->ctx, appstream_id);
7709
0
  fwupd_security_attr_set_plugin(attr, fu_device_get_plugin(FU_DEVICE(self)));
7710
0
  fwupd_security_attr_add_guids(attr, fu_device_get_guids(FU_DEVICE(self)));
7711
7712
  /* if the device is a child of the host firmware then add those GUIDs too */
7713
0
  if (fu_device_has_private_flag(self, FU_DEVICE_PRIVATE_FLAG_HOST_FIRMWARE_CHILD)) {
7714
0
    FuDevice *msf_device = fu_device_get_parent(self);
7715
0
    if (msf_device != NULL) {
7716
0
      GPtrArray *guids = fu_device_get_guids(msf_device);
7717
0
      for (guint i = 0; i < guids->len; i++) {
7718
0
        const gchar *guid = g_ptr_array_index(guids, i);
7719
0
        fwupd_security_attr_add_guid(attr, guid);
7720
0
      }
7721
0
    }
7722
0
  }
7723
7724
0
  return g_steal_pointer(&attr);
7725
0
}
7726
7727
static void
7728
fu_device_ensure_events(FuDevice *self)
7729
0
{
7730
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
7731
0
  if (priv->events != NULL)
7732
0
    return;
7733
0
  priv->events = g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref);
7734
0
}
7735
7736
/**
7737
 * fu_device_add_event:
7738
 * @self: a #FuDevice
7739
 * @event: (not nullable): a #FuDeviceEvent
7740
 *
7741
 * Adds an event to the device.
7742
 *
7743
 * Since: 2.0.0
7744
 **/
7745
void
7746
fu_device_add_event(FuDevice *self, FuDeviceEvent *event)
7747
0
{
7748
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
7749
7750
0
  g_return_if_fail(FU_IS_DEVICE(self));
7751
0
  g_return_if_fail(FU_IS_DEVICE_EVENT(event));
7752
7753
  /* redirect */
7754
0
  if (priv->target != NULL) {
7755
0
    fu_device_add_event(priv->target, event);
7756
0
    return;
7757
0
  }
7758
7759
0
  fu_device_ensure_events(self);
7760
0
  g_ptr_array_add(priv->events, g_object_ref(event));
7761
0
}
7762
7763
/**
7764
 * fu_device_save_event:
7765
 * @self: a #FuDevice
7766
 * @id: (nullable): the event ID, e.g. `usb:AA:AA:06`
7767
 *
7768
 * Creates a new event with a specific ID and adds it to the device.
7769
 *
7770
 * Returns: (transfer none): a #FuDeviceEvent
7771
 *
7772
 * Since: 2.0.0
7773
 **/
7774
FuDeviceEvent *
7775
fu_device_save_event(FuDevice *self, const gchar *id)
7776
0
{
7777
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
7778
0
  g_autoptr(FuDeviceEvent) event = NULL;
7779
7780
0
  g_return_val_if_fail(FU_IS_DEVICE(self), NULL);
7781
0
  g_return_val_if_fail(id != NULL, NULL);
7782
7783
  /* redirect */
7784
0
  if (priv->target != NULL)
7785
0
    return fu_device_save_event(priv->target, id);
7786
7787
  /* success */
7788
0
  event = fu_device_event_new(id);
7789
0
  fu_device_add_event(self, event);
7790
0
  g_debug("saved event %s", id);
7791
0
  return event;
7792
0
}
7793
7794
/**
7795
 * fu_device_load_event:
7796
 * @self: a #FuDevice
7797
 * @id: (not nullable): the event ID, e.g. `usb:AA:AA:06`
7798
 * @error: (nullable): optional return location for an error
7799
 *
7800
 * Loads a new event with a specific ID from the device.
7801
 *
7802
 * Returns: (transfer none) (nullable): a #FuDeviceEvent
7803
 *
7804
 * Since: 2.0.0
7805
 **/
7806
FuDeviceEvent *
7807
fu_device_load_event(FuDevice *self, const gchar *id, GError **error)
7808
0
{
7809
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
7810
0
  g_autofree gchar *id_hash = NULL;
7811
7812
0
  g_return_val_if_fail(FU_IS_DEVICE(self), NULL);
7813
0
  g_return_val_if_fail(id != NULL, NULL);
7814
0
  g_return_val_if_fail(error == NULL || *error == NULL, NULL);
7815
7816
  /* redirect */
7817
0
  if (priv->target != NULL)
7818
0
    return fu_device_load_event(priv->target, id, error);
7819
7820
  /* sanity check */
7821
0
  if (priv->events == NULL) {
7822
0
    g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND, "no events loaded");
7823
0
    return NULL;
7824
0
  }
7825
7826
  /* reset back to the beginning */
7827
0
  if (priv->event_idx >= priv->events->len) {
7828
0
    g_debug("resetting event index");
7829
0
    priv->event_idx = 0;
7830
0
  }
7831
7832
  /* look for the next event in the sequence */
7833
0
  id_hash = fu_device_event_build_id(id);
7834
0
  for (guint i = priv->event_idx; i < priv->events->len; i++) {
7835
0
    FuDeviceEvent *event = g_ptr_array_index(priv->events, i);
7836
0
    if (g_strcmp0(fu_device_event_get_id(event), id_hash) == 0) {
7837
0
      priv->event_idx = i + 1;
7838
0
      return event;
7839
0
    }
7840
0
  }
7841
7842
  /* look for *any* event that matches */
7843
0
  for (guint i = 0; i < priv->events->len; i++) {
7844
0
    FuDeviceEvent *event = g_ptr_array_index(priv->events, i);
7845
0
    if (g_strcmp0(fu_device_event_get_id(event), id_hash) == 0) {
7846
0
      g_set_error(error,
7847
0
            FWUPD_ERROR,
7848
0
            FWUPD_ERROR_NOT_FOUND,
7849
0
            "found out-of-order event %s at position %u",
7850
0
            id,
7851
0
            i);
7852
0
      return NULL;
7853
0
    }
7854
0
  }
7855
7856
  /* nothing found */
7857
0
  g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND, "no event with ID %s", id);
7858
0
  return NULL;
7859
0
}
7860
7861
/**
7862
 * fu_device_get_events:
7863
 * @self: a #FuDevice
7864
 *
7865
 * Gets all the #FuDeviceEvent objects added with fu_device_add_event().
7866
 *
7867
 * These events should be added by #FuDevice subclasses to enable the daemon to emulate a specific
7868
 * device type.
7869
 *
7870
 * Returns: (transfer none) (element-type FuDeviceEvent): events
7871
 *
7872
 * Since: 2.0.0
7873
 **/
7874
GPtrArray *
7875
fu_device_get_events(FuDevice *self)
7876
0
{
7877
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
7878
7879
0
  g_return_val_if_fail(FU_IS_DEVICE(self), NULL);
7880
7881
  /* redirect */
7882
0
  if (priv->target != NULL)
7883
0
    return fu_device_get_events(priv->target);
7884
7885
0
  fu_device_ensure_events(self);
7886
0
  return priv->events;
7887
0
}
7888
7889
/**
7890
 * fu_device_clear_events:
7891
 * @self: a #FuDevice
7892
 *
7893
 * Clears all the #FuDeviceEvent objects added with fu_device_add_event(), typically after saving
7894
 * the device to an emulation.
7895
 *
7896
 * Since: 2.0.0
7897
 **/
7898
void
7899
fu_device_clear_events(FuDevice *self)
7900
0
{
7901
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
7902
7903
0
  g_return_if_fail(FU_IS_DEVICE(self));
7904
7905
  /* redirect */
7906
0
  if (priv->target != NULL) {
7907
0
    fu_device_clear_events(priv->target);
7908
0
    return;
7909
0
  }
7910
7911
0
  if (priv->events == NULL)
7912
0
    return;
7913
0
  g_ptr_array_set_size(priv->events, 0);
7914
0
  priv->event_idx = 0;
7915
0
}
7916
7917
/**
7918
 * fu_device_set_target:
7919
 * @self: a #FuDevice
7920
 * @target: a #FuDevice
7921
 *
7922
 * Sets the target device where #FuDeviceEvent objects added to @self should actually be added.
7923
 *
7924
 * Any existing events added to @self are added immediately to @target.
7925
 *
7926
 * Since: 2.0.0
7927
 **/
7928
void
7929
fu_device_set_target(FuDevice *self, FuDevice *target)
7930
0
{
7931
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
7932
7933
0
  g_return_if_fail(FU_IS_DEVICE(self));
7934
0
  g_return_if_fail(FU_IS_DEVICE(target));
7935
7936
0
  fu_device_incorporate(target, self, FU_DEVICE_INCORPORATE_FLAG_EVENTS);
7937
0
  g_set_object(&priv->target, target);
7938
0
}
7939
7940
/* private; used to save an emulated device */
7941
void
7942
fu_device_add_json(FuDevice *self, JsonBuilder *builder, FwupdCodecFlags flags)
7943
0
{
7944
0
  FuDeviceClass *device_class = FU_DEVICE_GET_CLASS(self);
7945
7946
0
  if (fu_device_get_created_usec(self) != 0) {
7947
#if GLIB_CHECK_VERSION(2, 80, 0)
7948
    g_autoptr(GDateTime) dt =
7949
        g_date_time_new_from_unix_utc_usec(fu_device_get_created_usec(self));
7950
#else
7951
0
    g_autoptr(GDateTime) dt = g_date_time_new_from_unix_utc(
7952
0
        fu_device_get_created_usec(self) / G_USEC_PER_SEC);
7953
0
#endif
7954
0
    g_autofree gchar *str = g_date_time_format_iso8601(dt);
7955
0
    json_builder_set_member_name(builder, "Created");
7956
0
    json_builder_add_string_value(builder, str);
7957
0
  }
7958
7959
  /* subclassed */
7960
0
  if (device_class->add_json != NULL)
7961
0
    device_class->add_json(self, builder, flags);
7962
0
}
7963
7964
/* private; used to load an emulated device */
7965
gboolean
7966
fu_device_from_json(FuDevice *self, JsonObject *json_object, GError **error)
7967
0
{
7968
0
  const gchar *tmp;
7969
0
  FuDeviceClass *device_class = FU_DEVICE_GET_CLASS(self);
7970
7971
0
  tmp = json_object_get_string_member_with_default(json_object, "Created", NULL);
7972
0
  if (tmp != NULL) {
7973
0
    g_autoptr(GDateTime) dt = g_date_time_new_from_iso8601(tmp, NULL);
7974
#if GLIB_CHECK_VERSION(2, 80, 0)
7975
    if (dt != NULL)
7976
      fu_device_set_created_usec(self, g_date_time_to_unix_usec(dt));
7977
#else
7978
0
    if (dt != NULL) {
7979
0
      fu_device_set_created_usec(self, g_date_time_to_unix(dt) * G_USEC_PER_SEC);
7980
0
    }
7981
0
#endif
7982
0
  }
7983
7984
  /* subclassed */
7985
0
  if (device_class->from_json != NULL) {
7986
0
    if (!device_class->from_json(self, json_object, error))
7987
0
      return FALSE;
7988
0
  }
7989
7990
  /* success */
7991
0
  return TRUE;
7992
0
}
7993
7994
static void
7995
fu_device_dispose(GObject *object)
7996
0
{
7997
0
  FuDevice *self = FU_DEVICE(object);
7998
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
7999
0
  g_clear_object(&priv->ctx);
8000
0
  g_clear_object(&priv->target);
8001
0
  G_OBJECT_CLASS(fu_device_parent_class)->dispose(object);
8002
0
}
8003
8004
static void
8005
fu_device_class_init(FuDeviceClass *klass)
8006
0
{
8007
0
  GObjectClass *object_class = G_OBJECT_CLASS(klass);
8008
0
  FuDeviceClass *device_class = FU_DEVICE_CLASS(klass);
8009
0
  GParamSpec *pspec;
8010
0
  const gchar *quark_flags[] = {
8011
0
      FU_DEVICE_PRIVATE_FLAG_NO_PROBE,
8012
0
      FU_DEVICE_PRIVATE_FLAG_REFCOUNTED_PROXY,
8013
0
      FU_DEVICE_PRIVATE_FLAG_NO_GENERIC_GUIDS,
8014
0
      FU_DEVICE_PRIVATE_FLAG_NO_SERIAL_NUMBER,
8015
0
      FU_DEVICE_PRIVATE_FLAG_IS_FAKE,
8016
0
  };
8017
8018
0
  object_class->dispose = fu_device_dispose;
8019
0
  object_class->finalize = fu_device_finalize;
8020
0
  object_class->get_property = fu_device_get_property;
8021
0
  object_class->set_property = fu_device_set_property;
8022
8023
0
  device_class->to_string = fu_device_to_string_impl;
8024
8025
  /* used as device flags, order is important! */
8026
0
  for (guint i = 0; i < G_N_ELEMENTS(quark_flags); i++)
8027
0
    quarks[i] = g_quark_from_static_string(quark_flags[i]);
8028
8029
  /**
8030
   * FuDevice::child-added:
8031
   * @self: the #FuDevice instance that emitted the signal
8032
   * @device: the #FuDevice child
8033
   *
8034
   * The ::child-added signal is emitted when a device has been added as a child.
8035
   *
8036
   * Since: 1.0.8
8037
   **/
8038
0
  signals[SIGNAL_CHILD_ADDED] = g_signal_new("child-added",
8039
0
               G_TYPE_FROM_CLASS(object_class),
8040
0
               G_SIGNAL_RUN_LAST,
8041
0
               G_STRUCT_OFFSET(FuDeviceClass, child_added),
8042
0
               NULL,
8043
0
               NULL,
8044
0
               g_cclosure_marshal_VOID__OBJECT,
8045
0
               G_TYPE_NONE,
8046
0
               1,
8047
0
               FU_TYPE_DEVICE);
8048
  /**
8049
   * FuDevice::child-removed:
8050
   * @self: the #FuDevice instance that emitted the signal
8051
   * @device: the #FuDevice child
8052
   *
8053
   * The ::child-removed signal is emitted when a device has been removed as a child.
8054
   *
8055
   * Since: 1.0.8
8056
   **/
8057
0
  signals[SIGNAL_CHILD_REMOVED] = g_signal_new("child-removed",
8058
0
                 G_TYPE_FROM_CLASS(object_class),
8059
0
                 G_SIGNAL_RUN_LAST,
8060
0
                 G_STRUCT_OFFSET(FuDeviceClass, child_removed),
8061
0
                 NULL,
8062
0
                 NULL,
8063
0
                 g_cclosure_marshal_VOID__OBJECT,
8064
0
                 G_TYPE_NONE,
8065
0
                 1,
8066
0
                 FU_TYPE_DEVICE);
8067
  /**
8068
   * FuDevice::request:
8069
   * @self: the #FuDevice instance that emitted the signal
8070
   * @request: the #FwupdRequest
8071
   *
8072
   * The ::request signal is emitted when the device needs interactive action from the user.
8073
   *
8074
   * Since: 1.6.2
8075
   **/
8076
0
  signals[SIGNAL_REQUEST] = g_signal_new("request",
8077
0
                 G_TYPE_FROM_CLASS(object_class),
8078
0
                 G_SIGNAL_RUN_LAST,
8079
0
                 G_STRUCT_OFFSET(FuDeviceClass, request),
8080
0
                 NULL,
8081
0
                 NULL,
8082
0
                 g_cclosure_marshal_VOID__OBJECT,
8083
0
                 G_TYPE_NONE,
8084
0
                 1,
8085
0
                 FWUPD_TYPE_REQUEST);
8086
8087
  /**
8088
   * FuDevice:physical-id:
8089
   *
8090
   * The device physical ID.
8091
   *
8092
   * Since: 1.1.2
8093
   */
8094
0
  pspec = g_param_spec_string("physical-id",
8095
0
            NULL,
8096
0
            NULL,
8097
0
            NULL,
8098
0
            G_PARAM_READWRITE | G_PARAM_STATIC_NAME);
8099
0
  g_object_class_install_property(object_class, PROP_PHYSICAL_ID, pspec);
8100
8101
  /**
8102
   * FuDevice:logical-id:
8103
   *
8104
   * The device logical ID.
8105
   *
8106
   * Since: 1.1.2
8107
   */
8108
0
  pspec = g_param_spec_string("logical-id",
8109
0
            NULL,
8110
0
            NULL,
8111
0
            NULL,
8112
0
            G_PARAM_READWRITE | G_PARAM_STATIC_NAME);
8113
0
  g_object_class_install_property(object_class, PROP_LOGICAL_ID, pspec);
8114
8115
  /**
8116
   * FuDevice:backend-id:
8117
   *
8118
   * The device backend ID.
8119
   *
8120
   * Since: 1.5.8
8121
   */
8122
0
  pspec = g_param_spec_string("backend-id",
8123
0
            NULL,
8124
0
            NULL,
8125
0
            NULL,
8126
0
            G_PARAM_READWRITE | G_PARAM_STATIC_NAME);
8127
0
  g_object_class_install_property(object_class, PROP_BACKEND_ID, pspec);
8128
8129
  /**
8130
   * FuDevice:equivalent-id:
8131
   *
8132
   * The device equivalent ID.
8133
   *
8134
   * Since: 2.0.0
8135
   */
8136
0
  pspec = g_param_spec_string("equivalent-id",
8137
0
            NULL,
8138
0
            NULL,
8139
0
            NULL,
8140
0
            G_PARAM_READWRITE | G_PARAM_STATIC_NAME);
8141
0
  g_object_class_install_property(object_class, PROP_EQUIVALENT_ID, pspec);
8142
  /**
8143
   * FuDevice:update-message:
8144
   *
8145
   * The device update message.
8146
   *
8147
   * Since: 2.0.0
8148
   */
8149
0
  pspec = g_param_spec_string("update-message",
8150
0
            NULL,
8151
0
            NULL,
8152
0
            NULL,
8153
0
            G_PARAM_READWRITE | G_PARAM_STATIC_NAME);
8154
0
  g_object_class_install_property(object_class, PROP_UPDATE_MESSAGE, pspec);
8155
8156
  /**
8157
   * FuDevice:update-image:
8158
   *
8159
   * The update image for the device.
8160
   *
8161
   * Since: 2.0.0
8162
   */
8163
0
  pspec = g_param_spec_string("update-image",
8164
0
            NULL,
8165
0
            NULL,
8166
0
            NULL,
8167
0
            G_PARAM_READWRITE | G_PARAM_STATIC_NAME);
8168
0
  g_object_class_install_property(object_class, PROP_UPDATE_IMAGE, pspec);
8169
8170
  /**
8171
   * FuDevice:context:
8172
   *
8173
   * The #FuContext to use.
8174
   *
8175
   * Since: 1.6.0
8176
   */
8177
0
  pspec = g_param_spec_object("context",
8178
0
            NULL,
8179
0
            NULL,
8180
0
            FU_TYPE_CONTEXT,
8181
0
            G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_NAME);
8182
0
  g_object_class_install_property(object_class, PROP_CONTEXT, pspec);
8183
8184
  /**
8185
   * FuDevice:backend:
8186
   *
8187
   * The #FuBackend that created the device.
8188
   *
8189
   * Since: 2.0.0
8190
   */
8191
0
  pspec = g_param_spec_object("backend",
8192
0
            NULL,
8193
0
            NULL,
8194
0
            FU_TYPE_BACKEND,
8195
0
            G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_NAME);
8196
0
  g_object_class_install_property(object_class, PROP_BACKEND, pspec);
8197
8198
  /**
8199
   * FuDevice:proxy:
8200
   *
8201
   * The device proxy to use.
8202
   *
8203
   * Since: 1.4.1
8204
   */
8205
0
  pspec = g_param_spec_object("proxy",
8206
0
            NULL,
8207
0
            NULL,
8208
0
            FU_TYPE_DEVICE,
8209
0
            G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_NAME);
8210
0
  g_object_class_install_property(object_class, PROP_PROXY, pspec);
8211
8212
  /**
8213
   * FuDevice:parent:
8214
   *
8215
   * The device parent.
8216
   *
8217
   * Since: 1.0.8
8218
   */
8219
0
  pspec = g_param_spec_object("parent",
8220
0
            NULL,
8221
0
            NULL,
8222
0
            FU_TYPE_DEVICE,
8223
0
            G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_NAME);
8224
0
  g_object_class_install_property(object_class, PROP_PARENT, pspec);
8225
8226
  /**
8227
   * FuDevice:private-flags:
8228
   *
8229
   * The device private flags.
8230
   *
8231
   * Since: 1.9.1
8232
   */
8233
0
  pspec = g_param_spec_uint64("private-flags",
8234
0
            NULL,
8235
0
            NULL,
8236
0
            0,
8237
0
            G_MAXUINT64,
8238
0
            0,
8239
0
            G_PARAM_READABLE | G_PARAM_STATIC_NAME);
8240
0
  g_object_class_install_property(object_class, PROP_PRIVATE_FLAGS, pspec);
8241
8242
  /**
8243
   * FuDevice:vid:
8244
   *
8245
   * The device vendor ID.
8246
   *
8247
   * Since: 2.0.0
8248
   */
8249
0
  pspec = g_param_spec_uint("vid",
8250
0
          NULL,
8251
0
          NULL,
8252
0
          0,
8253
0
          G_MAXUINT16,
8254
0
          0,
8255
0
          G_PARAM_READWRITE | G_PARAM_STATIC_NAME);
8256
0
  g_object_class_install_property(object_class, PROP_VID, pspec);
8257
8258
  /**
8259
   * FuDevice:pid:
8260
   *
8261
   * The device product ID.
8262
   *
8263
   * Since: 2.0.0
8264
   */
8265
0
  pspec = g_param_spec_uint("pid",
8266
0
          NULL,
8267
0
          NULL,
8268
0
          0,
8269
0
          G_MAXUINT16,
8270
0
          0,
8271
0
          G_PARAM_READWRITE | G_PARAM_STATIC_NAME);
8272
0
  g_object_class_install_property(object_class, PROP_PID, pspec);
8273
8274
  /**
8275
   * FuDevice:required-free:
8276
   *
8277
   * The required amount of free firmware space.
8278
   *
8279
   * Since: 2.0.12
8280
   */
8281
0
  pspec = g_param_spec_uint64("required-free",
8282
0
            NULL,
8283
0
            NULL,
8284
0
            0,
8285
0
            G_MAXUINT64,
8286
0
            0,
8287
0
            G_PARAM_READWRITE | G_PARAM_STATIC_NAME);
8288
0
  g_object_class_install_property(object_class, PROP_REQUIRED_FREE, pspec);
8289
0
}
8290
8291
static void
8292
fu_device_init(FuDevice *self)
8293
0
{
8294
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
8295
0
  priv->order = G_MAXINT;
8296
0
  priv->possible_plugins = g_ptr_array_new_with_free_func(g_free);
8297
0
  priv->acquiesce_delay = 50; /* ms */
8298
0
  priv->private_flags_registered = g_array_new(FALSE, FALSE, sizeof(GQuark));
8299
0
  priv->private_flags = g_array_new(FALSE, FALSE, sizeof(GQuark));
8300
0
}
8301
8302
static void
8303
fu_device_finalize(GObject *object)
8304
0
{
8305
0
  FuDevice *self = FU_DEVICE(object);
8306
0
  FuDevicePrivate *priv = GET_PRIVATE(self);
8307
8308
0
  if (priv->progress != NULL)
8309
0
    g_object_unref(priv->progress);
8310
0
  if (priv->proxy != NULL) {
8311
0
    if (priv->notify_flags_proxy_id != 0)
8312
0
      g_signal_handler_disconnect(priv->proxy, priv->notify_flags_proxy_id);
8313
0
    if (fu_device_has_private_flag_quark(self, quarks[QUARK_REFCOUNTED_PROXY])) {
8314
0
      g_object_unref(priv->proxy);
8315
0
    } else {
8316
0
      g_object_remove_weak_pointer(G_OBJECT(priv->proxy),
8317
0
                 (gpointer *)&priv->proxy);
8318
0
    }
8319
0
  }
8320
0
  if (priv->backend != NULL)
8321
0
    g_object_remove_weak_pointer(G_OBJECT(priv->backend), (gpointer *)&priv->backend);
8322
0
  if (priv->poll_id != 0)
8323
0
    g_source_remove(priv->poll_id);
8324
0
  if (priv->metadata != NULL)
8325
0
    g_hash_table_unref(priv->metadata);
8326
0
  if (priv->inhibits != NULL)
8327
0
    g_hash_table_unref(priv->inhibits);
8328
0
  if (priv->instance_hash != NULL)
8329
0
    g_hash_table_unref(priv->instance_hash);
8330
0
  if (priv->parent_physical_ids != NULL)
8331
0
    g_ptr_array_unref(priv->parent_physical_ids);
8332
0
  if (priv->parent_backend_ids != NULL)
8333
0
    g_ptr_array_unref(priv->parent_backend_ids);
8334
0
  if (priv->events != NULL)
8335
0
    g_ptr_array_unref(priv->events);
8336
0
  if (priv->retry_recs != NULL)
8337
0
    g_ptr_array_unref(priv->retry_recs);
8338
0
  if (priv->instance_ids != NULL)
8339
0
    g_ptr_array_unref(priv->instance_ids);
8340
0
  if (priv->parent_guids != NULL)
8341
0
    g_ptr_array_unref(priv->parent_guids);
8342
0
  g_array_unref(priv->private_flags);
8343
0
  g_array_unref(priv->private_flags_registered);
8344
0
  g_ptr_array_unref(priv->possible_plugins);
8345
0
  g_free(priv->equivalent_id);
8346
0
  g_free(priv->physical_id);
8347
0
  g_free(priv->logical_id);
8348
0
  g_free(priv->backend_id);
8349
0
  g_free(priv->update_request_id);
8350
0
  g_free(priv->update_message);
8351
0
  g_free(priv->update_image);
8352
0
  g_free(priv->fwupd_version);
8353
0
  g_free(priv->proxy_guid);
8354
0
  g_free(priv->custom_flags);
8355
8356
0
  G_OBJECT_CLASS(fu_device_parent_class)->finalize(object);
8357
0
}
8358
8359
/**
8360
 * fu_device_new:
8361
 *
8362
 * Creates a new #Fudevice
8363
 *
8364
 * Since: 1.8.2
8365
 **/
8366
FuDevice *
8367
fu_device_new(FuContext *ctx)
8368
0
{
8369
0
  FuDevice *self = g_object_new(FU_TYPE_DEVICE, "context", ctx, NULL);
8370
0
  return FU_DEVICE(self);
8371
0
}