Coverage Report

Created: 2025-12-14 06:56

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