Coverage Report

Created: 2025-11-24 06:59

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