Coverage Report

Created: 2025-08-29 06:48

/src/fwupd/libfwupdplugin/fu-udev-device.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright 2017 Richard Hughes <richard@hughsie.com>
3
 *
4
 * SPDX-License-Identifier: LGPL-2.1-or-later
5
 */
6
7
0
#define G_LOG_DOMAIN "FuUdevDevice"
8
9
#include "config.h"
10
11
#include <fcntl.h>
12
#include <string.h>
13
#ifdef HAVE_ERRNO_H
14
#include <errno.h>
15
#endif
16
#ifdef HAVE_IOCTL_H
17
#include <sys/ioctl.h>
18
#endif
19
#include <glib/gstdio.h>
20
#include <sys/stat.h>
21
#include <sys/types.h>
22
#include <unistd.h>
23
24
#include "fu-device-event-private.h"
25
#include "fu-device-private.h"
26
#include "fu-ioctl-private.h"
27
#include "fu-output-stream.h"
28
#include "fu-path.h"
29
#include "fu-string.h"
30
#include "fu-udev-device-private.h"
31
32
/**
33
 * FuUdevDevice:
34
 *
35
 * A UDev device, typically only available on Linux.
36
 *
37
 * See also: [class@FuDevice]
38
 */
39
40
typedef struct {
41
  gchar *subsystem;
42
  gchar *bind_id;
43
  gchar *driver;
44
  gchar *device_file;
45
  gchar *devtype;
46
  guint64 number;
47
  FuIOChannel *io_channel;
48
  FuIoChannelOpenFlags open_flags;
49
  GHashTable *properties;
50
  gboolean properties_valid;
51
} FuUdevDevicePrivate;
52
53
G_DEFINE_TYPE_WITH_PRIVATE(FuUdevDevice, fu_udev_device, FU_TYPE_DEVICE);
54
55
enum {
56
  PROP_0,
57
  PROP_SUBSYSTEM,
58
  PROP_DRIVER,
59
  PROP_DEVICE_FILE,
60
  PROP_BIND_ID,
61
  PROP_DEVTYPE,
62
  PROP_LAST
63
};
64
65
enum { SIGNAL_CHANGED, SIGNAL_LAST };
66
67
static guint signals[SIGNAL_LAST] = {0};
68
69
0
#define GET_PRIVATE(o) (fu_udev_device_get_instance_private(o))
70
71
/**
72
 * fu_udev_device_emit_changed:
73
 * @self: a #FuUdevDevice
74
 *
75
 * Emits the ::changed signal for the object.
76
 *
77
 * Since: 1.1.2
78
 **/
79
void
80
fu_udev_device_emit_changed(FuUdevDevice *self)
81
0
{
82
0
  g_autoptr(GError) error = NULL;
83
0
  g_return_if_fail(FU_IS_UDEV_DEVICE(self));
84
0
  g_debug("FuUdevDevice emit changed");
85
0
  if (!fu_device_rescan(FU_DEVICE(self), &error))
86
0
    g_debug("%s", error->message);
87
0
  g_signal_emit(self, signals[SIGNAL_CHANGED], 0);
88
0
}
89
90
static void
91
fu_udev_device_to_string(FuDevice *device, guint idt, GString *str)
92
0
{
93
0
  FuUdevDevice *self = FU_UDEV_DEVICE(device);
94
0
  FuUdevDevicePrivate *priv = GET_PRIVATE(self);
95
0
  g_autofree gchar *open_flags = fu_io_channel_open_flags_to_string(priv->open_flags);
96
97
0
  fwupd_codec_string_append_hex(str, idt, "Number", priv->number);
98
0
  fwupd_codec_string_append(str, idt, "Subsystem", priv->subsystem);
99
0
  fwupd_codec_string_append(str, idt, "Devtype", priv->devtype);
100
0
  fwupd_codec_string_append(str, idt, "Driver", priv->driver);
101
0
  fwupd_codec_string_append(str, idt, "BindId", priv->bind_id);
102
0
  fwupd_codec_string_append(str, idt, "DeviceFile", priv->device_file);
103
0
  fwupd_codec_string_append(str, idt, "OpenFlags", open_flags);
104
0
}
105
106
static gboolean
107
fu_udev_device_ensure_bind_id(FuUdevDevice *self, GError **error)
108
0
{
109
0
  FuUdevDevicePrivate *priv = GET_PRIVATE(self);
110
111
  /* sanity check */
112
0
  if (priv->bind_id != NULL)
113
0
    return TRUE;
114
115
  /* automatically set the bind ID from the subsystem */
116
0
  if (g_strcmp0(priv->subsystem, "pci") == 0) {
117
0
    priv->bind_id = fu_udev_device_read_property(self, "PCI_SLOT_NAME", error);
118
0
    return priv->bind_id != NULL;
119
0
  }
120
0
  if (g_strcmp0(priv->subsystem, "hid") == 0) {
121
0
    priv->bind_id = fu_udev_device_read_property(self, "HID_PHYS", error);
122
0
    return priv->bind_id != NULL;
123
0
  }
124
0
  if (g_strcmp0(priv->subsystem, "usb") == 0) {
125
0
    priv->bind_id = g_path_get_basename(fu_udev_device_get_sysfs_path(self));
126
0
    return TRUE;
127
0
  }
128
129
  /* nothing found automatically */
130
0
  g_set_error(error,
131
0
        FWUPD_ERROR,
132
0
        FWUPD_ERROR_NOT_SUPPORTED,
133
0
        "cannot derive bind-id from subsystem %s",
134
0
        priv->subsystem);
135
0
  return FALSE;
136
0
}
137
138
/* private */
139
void
140
fu_udev_device_set_subsystem(FuUdevDevice *self, const gchar *subsystem)
141
0
{
142
0
  FuUdevDevicePrivate *priv = GET_PRIVATE(self);
143
144
  /* not changed */
145
0
  if (g_strcmp0(priv->subsystem, subsystem) == 0)
146
0
    return;
147
148
0
  g_free(priv->subsystem);
149
0
  priv->subsystem = g_strdup(subsystem);
150
0
  g_object_notify(G_OBJECT(self), "subsystem");
151
0
}
152
153
/**
154
 * fu_udev_device_set_bind_id:
155
 * @self: a #FuUdevDevice
156
 * @bind_id: a bind-id string, e.g. `pci:0:0:1`
157
 *
158
 * Sets the device ID used for binding the device, e.g. `pci:1:2:3`
159
 *
160
 * Since: 1.7.2
161
 **/
162
void
163
fu_udev_device_set_bind_id(FuUdevDevice *self, const gchar *bind_id)
164
0
{
165
0
  FuUdevDevicePrivate *priv = GET_PRIVATE(self);
166
167
  /* not changed */
168
0
  if (g_strcmp0(priv->bind_id, bind_id) == 0)
169
0
    return;
170
171
0
  g_free(priv->bind_id);
172
0
  priv->bind_id = g_strdup(bind_id);
173
0
  g_object_notify(G_OBJECT(self), "bind-id");
174
0
}
175
176
static void
177
fu_udev_device_set_driver(FuUdevDevice *self, const gchar *driver)
178
0
{
179
0
  FuUdevDevicePrivate *priv = GET_PRIVATE(self);
180
181
  /* not changed */
182
0
  if (g_strcmp0(priv->driver, driver) == 0)
183
0
    return;
184
185
0
  g_free(priv->driver);
186
0
  priv->driver = g_strdup(driver);
187
0
  g_object_notify(G_OBJECT(self), "driver");
188
0
}
189
190
/**
191
 * fu_udev_device_set_device_file:
192
 * @self: a #FuUdevDevice
193
 * @device_file: (nullable): a device path
194
 *
195
 * Sets the device file to use for reading and writing.
196
 *
197
 * Since: 1.8.7
198
 **/
199
void
200
fu_udev_device_set_device_file(FuUdevDevice *self, const gchar *device_file)
201
0
{
202
0
  FuUdevDevicePrivate *priv = GET_PRIVATE(self);
203
204
  /* not changed */
205
0
  if (g_strcmp0(priv->device_file, device_file) == 0)
206
0
    return;
207
208
0
  g_free(priv->device_file);
209
0
  priv->device_file = g_strdup(device_file);
210
0
  g_object_notify(G_OBJECT(self), "device-file");
211
0
}
212
213
static gchar *
214
fu_udev_device_get_symlink_target(FuUdevDevice *self, const gchar *attr, GError **error)
215
0
{
216
0
  FuDeviceEvent *event = NULL;
217
0
  g_autofree gchar *event_id = NULL;
218
0
  g_autofree gchar *fn_attr = NULL;
219
0
  g_autofree gchar *symlink_target = NULL;
220
0
  g_autofree gchar *value = NULL;
221
222
0
  if (fu_udev_device_get_sysfs_path(self) == NULL) {
223
0
    g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND, "no sysfs path");
224
0
    return NULL;
225
0
  }
226
227
  /* need event ID */
228
0
  if (fu_device_has_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_EMULATED) ||
229
0
      fu_context_has_flag(fu_device_get_context(FU_DEVICE(self)),
230
0
        FU_CONTEXT_FLAG_SAVE_EVENTS)) {
231
0
    event_id = g_strdup_printf("GetSymlinkTarget:Attr=%s", attr);
232
0
  }
233
234
  /* emulated */
235
0
  if (fu_device_has_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_EMULATED)) {
236
0
    event = fu_device_load_event(FU_DEVICE(self), event_id, error);
237
0
    if (event == NULL)
238
0
      return NULL;
239
0
    return g_strdup(fu_device_event_get_str(event, "Data", error));
240
0
  }
241
242
  /* save */
243
0
  if (event_id != NULL)
244
0
    event = fu_device_save_event(FU_DEVICE(self), event_id);
245
246
  /* find target */
247
0
  fn_attr = g_build_filename(fu_udev_device_get_sysfs_path(self), attr, NULL);
248
0
  symlink_target = fu_path_get_symlink_target(fn_attr, error);
249
0
  if (symlink_target == NULL)
250
0
    return NULL;
251
0
  value = g_path_get_basename(symlink_target);
252
253
  /* save response */
254
0
  if (event != NULL)
255
0
    fu_device_event_set_str(event, "Data", value);
256
257
  /* success */
258
0
  return g_steal_pointer(&value);
259
0
}
260
261
/* private */
262
void
263
fu_udev_device_set_number(FuUdevDevice *self, guint64 number)
264
0
{
265
0
  FuUdevDevicePrivate *priv = GET_PRIVATE(self);
266
0
  g_return_if_fail(FU_IS_UDEV_DEVICE(self));
267
0
  priv->number = number;
268
0
}
269
270
/* private */
271
void
272
fu_udev_device_set_devtype(FuUdevDevice *self, const gchar *devtype)
273
0
{
274
0
  FuUdevDevicePrivate *priv = GET_PRIVATE(self);
275
276
  /* not changed */
277
0
  if (g_strcmp0(priv->devtype, devtype) == 0)
278
0
    return;
279
280
0
  g_free(priv->devtype);
281
0
  priv->devtype = g_strdup(devtype);
282
0
  g_object_notify(G_OBJECT(self), "devtype");
283
0
}
284
285
/* private */
286
gboolean
287
fu_udev_device_parse_number(FuUdevDevice *self, GError **error)
288
0
{
289
0
  FuUdevDevicePrivate *priv = GET_PRIVATE(self);
290
0
  g_autoptr(GString) path = g_string_new(fu_udev_device_get_sysfs_path(self));
291
292
0
  if (path->len == 0)
293
0
    return TRUE;
294
0
  for (guint i = path->len - 1; i > 0; i--) {
295
0
    if (!g_ascii_isdigit(path->str[i])) {
296
0
      g_string_erase(path, 0, i + 1);
297
0
      break;
298
0
    }
299
0
  }
300
0
  if (path->len > 0) {
301
0
    if (!fu_strtoull(path->str,
302
0
         &priv->number,
303
0
         0x0,
304
0
         G_MAXUINT64,
305
0
         FU_INTEGER_BASE_AUTO,
306
0
         error))
307
0
      return FALSE;
308
0
  }
309
310
  /* success */
311
0
  return TRUE;
312
0
}
313
314
static gboolean
315
fu_udev_device_ensure_devtype_from_modalias(FuUdevDevice *self, GError **error)
316
0
{
317
0
  FuUdevDevicePrivate *priv = GET_PRIVATE(self);
318
0
  const gchar *devtype_modalias[] = {"mmc", "platform", NULL};
319
0
  g_autofree gchar *prop_modalias = NULL;
320
0
  g_auto(GStrv) split_modalias = NULL;
321
322
  /* only some subsystems forget to set the DEVTYPE property */
323
0
  if (!g_strv_contains(devtype_modalias, priv->subsystem))
324
0
    return TRUE;
325
326
  /* parse out subsystem:devtype */
327
0
  prop_modalias = fu_udev_device_read_property(self, "MODALIAS", error);
328
0
  if (prop_modalias == NULL)
329
0
    return FALSE;
330
0
  split_modalias = g_strsplit(prop_modalias, ":", 2);
331
0
  if (g_strv_length(split_modalias) >= 2)
332
0
    priv->devtype = g_strdup(split_modalias[1]);
333
334
  /* success */
335
0
  return TRUE;
336
0
}
337
338
static gboolean
339
fu_udev_device_probe(FuDevice *device, GError **error)
340
0
{
341
0
  FuUdevDevice *self = FU_UDEV_DEVICE(device);
342
0
  FuUdevDevicePrivate *priv = GET_PRIVATE(self);
343
0
  g_autofree gchar *subsystem = NULL;
344
0
  g_autofree gchar *attr_device = NULL;
345
0
  g_autofree gchar *attr_vendor = NULL;
346
347
  /* find the subsystem, driver and devtype */
348
0
  if (priv->subsystem == NULL) {
349
0
    g_autofree gchar *subsystem_tmp =
350
0
        fu_udev_device_get_symlink_target(self, "subsystem", error);
351
0
    if (subsystem_tmp == NULL) {
352
0
      g_prefix_error_literal(error, "failed to read subsystem: ");
353
0
      return FALSE;
354
0
    }
355
0
    fu_udev_device_set_subsystem(self, subsystem_tmp);
356
0
  }
357
0
  if (priv->driver == NULL)
358
0
    priv->driver = fu_udev_device_get_symlink_target(self, "driver", NULL);
359
0
  if (priv->devtype == NULL) {
360
0
    priv->devtype = fu_udev_device_read_property(self, "DEVTYPE", NULL);
361
0
    if (priv->devtype == NULL) {
362
0
      if (!fu_udev_device_ensure_devtype_from_modalias(self, error))
363
0
        return FALSE;
364
0
    }
365
0
  }
366
0
  if (priv->device_file == NULL) {
367
0
    g_autofree gchar *prop_devname =
368
0
        fu_udev_device_read_property(self, "DEVNAME", NULL);
369
0
    if (prop_devname != NULL) {
370
0
      g_autofree gchar *device_file = g_strdup_printf("/dev/%s", prop_devname);
371
0
      fu_udev_device_set_device_file(self, device_file);
372
0
    }
373
0
  }
374
375
  /* get IDs */
376
0
  attr_vendor = fu_udev_device_read_sysfs(self,
377
0
            "vendor",
378
0
            FU_UDEV_DEVICE_ATTR_READ_TIMEOUT_DEFAULT,
379
0
            NULL);
380
0
  if (attr_vendor != NULL) {
381
0
    guint64 tmp64 = 0;
382
0
    if (!fu_strtoull(attr_vendor, &tmp64, 0, G_MAXUINT16, FU_INTEGER_BASE_AUTO, NULL)) {
383
0
      fu_device_set_vendor(device, attr_vendor);
384
0
    } else {
385
0
      fu_device_set_vid(device, (guint16)tmp64);
386
0
    }
387
0
  }
388
0
  attr_device = fu_udev_device_read_sysfs(self,
389
0
            "device",
390
0
            FU_UDEV_DEVICE_ATTR_READ_TIMEOUT_DEFAULT,
391
0
            NULL);
392
0
  if (attr_device != NULL) {
393
0
    guint64 tmp64 = 0;
394
0
    if (!fu_strtoull(attr_device, &tmp64, 0, G_MAXUINT16, FU_INTEGER_BASE_AUTO, error))
395
0
      return FALSE;
396
0
    fu_device_set_pid(device, (guint16)tmp64);
397
0
  }
398
399
  /* set number */
400
0
  if (fu_udev_device_get_sysfs_path(self) != NULL) {
401
0
    g_autoptr(GError) error_local = NULL;
402
0
    if (!fu_udev_device_parse_number(self, &error_local))
403
0
      g_debug("failed to convert udev number: %s", error_local->message);
404
0
  }
405
406
  /* set vendor ID */
407
0
  if (priv->subsystem != NULL)
408
0
    subsystem = g_ascii_strup(priv->subsystem, -1);
409
0
  if (subsystem != NULL)
410
0
    fu_device_build_vendor_id_u16(device, subsystem, fu_device_get_vid(device));
411
412
  /* add GUIDs in order of priority */
413
0
  if (subsystem != NULL) {
414
0
    fu_device_build_instance_id_full(device,
415
0
             FU_DEVICE_INSTANCE_FLAG_GENERIC |
416
0
                 FU_DEVICE_INSTANCE_FLAG_QUIRKS,
417
0
             NULL,
418
0
             subsystem,
419
0
             "VEN",
420
0
             NULL);
421
0
    fu_device_build_instance_id_full(device,
422
0
             FU_DEVICE_INSTANCE_FLAG_GENERIC |
423
0
                 FU_DEVICE_INSTANCE_FLAG_VISIBLE |
424
0
                 FU_DEVICE_INSTANCE_FLAG_QUIRKS,
425
0
             NULL,
426
0
             subsystem,
427
0
             "VEN",
428
0
             "DEV",
429
0
             NULL);
430
0
  }
431
432
  /* add device class */
433
0
  if (subsystem != NULL) {
434
0
    g_autofree gchar *cls =
435
0
        fu_udev_device_read_sysfs(self,
436
0
                "class",
437
0
                FU_UDEV_DEVICE_ATTR_READ_TIMEOUT_DEFAULT,
438
0
                NULL);
439
0
    g_autofree gchar *devtype = fu_udev_device_read_property(self, "DEVTYPE", NULL);
440
0
    if (cls != NULL && g_str_has_prefix(cls, "0x"))
441
0
      fu_device_add_instance_strup(device, "CLASS", cls + 2);
442
0
    else
443
0
      fu_device_add_instance_strup(device, "CLASS", cls);
444
0
    fu_device_build_instance_id_full(device,
445
0
             FU_DEVICE_INSTANCE_FLAG_GENERIC |
446
0
                 FU_DEVICE_INSTANCE_FLAG_QUIRKS,
447
0
             NULL,
448
0
             subsystem,
449
0
             "VEN",
450
0
             "CLASS",
451
0
             NULL);
452
453
    /* add devtype */
454
0
    fu_device_add_instance_strup(device, "TYPE", devtype);
455
0
    fu_device_build_instance_id_full(device,
456
0
             FU_DEVICE_INSTANCE_FLAG_GENERIC |
457
0
                 FU_DEVICE_INSTANCE_FLAG_QUIRKS,
458
0
             NULL,
459
0
             subsystem,
460
0
             "TYPE",
461
0
             NULL);
462
463
    /* add the driver */
464
0
    fu_device_add_instance_str(device, "DRIVER", priv->driver);
465
0
    fu_device_build_instance_id_full(device,
466
0
             FU_DEVICE_INSTANCE_FLAG_GENERIC |
467
0
                 FU_DEVICE_INSTANCE_FLAG_QUIRKS,
468
0
             NULL,
469
0
             subsystem,
470
0
             "DRIVER",
471
0
             NULL);
472
0
  }
473
474
  /* success */
475
0
  return TRUE;
476
0
}
477
478
/**
479
 * fu_udev_device_get_subsystem_depth:
480
 * @self: a #FuUdevDevice
481
 * @subsystem: a subsystem
482
 *
483
 * Determine how far up a chain a given device is
484
 *
485
 * Returns: unsigned integer
486
 *
487
 * Since: 2.0.0
488
 **/
489
guint
490
fu_udev_device_get_subsystem_depth(FuUdevDevice *self, const gchar *subsystem)
491
0
{
492
0
  g_autoptr(FuDevice) device_tmp = NULL;
493
494
0
  device_tmp = fu_device_get_backend_parent_with_subsystem(FU_DEVICE(self), subsystem, NULL);
495
0
  if (device_tmp == NULL)
496
0
    return 0;
497
0
  if (g_strcmp0(fu_device_get_id(device_tmp), fu_device_get_id(FU_DEVICE(self))) == 0)
498
0
    return 0;
499
0
  for (guint i = 0;; i++) {
500
0
    g_autoptr(FuDevice) parent =
501
0
        fu_device_get_backend_parent(FU_DEVICE(device_tmp), NULL);
502
0
    if (parent == NULL)
503
0
      return i;
504
0
    g_set_object(&device_tmp, parent);
505
0
  }
506
0
  return 0;
507
0
}
508
509
static void
510
fu_udev_device_probe_complete(FuDevice *device)
511
0
{
512
0
  FuUdevDevice *self = FU_UDEV_DEVICE(device);
513
0
  FuUdevDevicePrivate *priv = GET_PRIVATE(self);
514
0
  g_hash_table_remove_all(priv->properties);
515
0
  priv->properties_valid = FALSE;
516
0
}
517
518
static gboolean
519
fu_udev_device_unbind_driver(FuDevice *device, GError **error)
520
0
{
521
0
  FuUdevDevice *self = FU_UDEV_DEVICE(device);
522
0
  FuUdevDevicePrivate *priv = GET_PRIVATE(self);
523
0
  g_autofree gchar *fn = NULL;
524
0
  g_autoptr(GOutputStream) stream = NULL;
525
526
  /* emulated */
527
0
  if (fu_device_has_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_EMULATED))
528
0
    return TRUE;
529
530
  /* is already unbound */
531
0
  if (fu_udev_device_get_sysfs_path(self) == NULL) {
532
0
    g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND, "not initialized");
533
0
    return FALSE;
534
0
  }
535
0
  fn = g_build_filename(fu_udev_device_get_sysfs_path(self), "driver", "unbind", NULL);
536
0
  if (!g_file_test(fn, G_FILE_TEST_EXISTS))
537
0
    return TRUE;
538
539
  /* write bus ID to file */
540
0
  if (!fu_udev_device_ensure_bind_id(self, error))
541
0
    return FALSE;
542
0
  stream = fu_output_stream_from_path(fn, error);
543
0
  if (stream == NULL)
544
0
    return FALSE;
545
0
  return g_output_stream_write_all(stream,
546
0
           priv->bind_id,
547
0
           strlen(priv->bind_id),
548
0
           NULL,
549
0
           NULL,
550
0
           error);
551
0
}
552
553
static gboolean
554
fu_udev_device_bind_driver(FuDevice *device,
555
         const gchar *subsystem,
556
         const gchar *driver,
557
         GError **error)
558
0
{
559
0
  FuUdevDevice *self = FU_UDEV_DEVICE(device);
560
0
  FuUdevDevicePrivate *priv = GET_PRIVATE(self);
561
0
  g_autofree gchar *driver_safe = g_strdup(driver);
562
0
  g_autofree gchar *fn = NULL;
563
0
  g_autoptr(GFile) file = NULL;
564
0
  g_autoptr(GOutputStream) stream = NULL;
565
566
  /* emulated */
567
0
  if (fu_device_has_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_EMULATED))
568
0
    return TRUE;
569
570
  /* copy the logic from modprobe */
571
0
  g_strdelimit(driver_safe, "-", '_');
572
573
  /* driver exists */
574
0
  fn = g_strdup_printf("/sys/module/%s/drivers/%s:%s/bind",
575
0
           driver_safe,
576
0
           subsystem,
577
0
           driver_safe);
578
0
  if (!g_file_test(fn, G_FILE_TEST_EXISTS)) {
579
0
    g_set_error(error,
580
0
          FWUPD_ERROR,
581
0
          FWUPD_ERROR_NOT_SUPPORTED,
582
0
          "cannot bind with %s:%s",
583
0
          subsystem,
584
0
          driver);
585
0
    return FALSE;
586
0
  }
587
588
  /* write bus ID to file */
589
0
  if (!fu_udev_device_ensure_bind_id(self, error))
590
0
    return FALSE;
591
0
  if (priv->bind_id == NULL) {
592
0
    g_set_error(error,
593
0
          FWUPD_ERROR,
594
0
          FWUPD_ERROR_NOT_SUPPORTED,
595
0
          "bind-id not set for subsystem %s",
596
0
          priv->subsystem);
597
0
    return FALSE;
598
0
  }
599
0
  file = g_file_new_for_path(fn);
600
0
  stream =
601
0
      G_OUTPUT_STREAM(g_file_replace(file, NULL, FALSE, G_FILE_CREATE_NONE, NULL, error));
602
0
  if (stream == NULL)
603
0
    return FALSE;
604
0
  return g_output_stream_write_all(stream,
605
0
           priv->bind_id,
606
0
           strlen(priv->bind_id),
607
0
           NULL,
608
0
           NULL,
609
0
           error);
610
0
}
611
612
static FuIoChannelOpenFlags
613
fu_udev_device_get_open_flags(FuUdevDevice *self)
614
0
{
615
0
  FuUdevDevicePrivate *priv = GET_PRIVATE(self);
616
0
  g_return_val_if_fail(FU_IS_UDEV_DEVICE(self), 0);
617
0
  return priv->open_flags;
618
0
}
619
620
static void
621
fu_udev_device_invalidate(FuDevice *device)
622
0
{
623
0
  FuUdevDevice *self = FU_UDEV_DEVICE(device);
624
0
  FuUdevDevicePrivate *priv = GET_PRIVATE(self);
625
0
  priv->properties_valid = FALSE;
626
0
  g_hash_table_remove_all(priv->properties);
627
0
}
628
629
static void
630
fu_udev_device_incorporate(FuDevice *self, FuDevice *donor)
631
0
{
632
0
  FuUdevDevice *uself = FU_UDEV_DEVICE(self);
633
0
  FuUdevDevice *udonor = FU_UDEV_DEVICE(donor);
634
0
  FuUdevDevicePrivate *priv = GET_PRIVATE(uself);
635
636
0
  g_return_if_fail(FU_IS_UDEV_DEVICE(self));
637
0
  g_return_if_fail(FU_IS_UDEV_DEVICE(donor));
638
639
0
  if (priv->device_file == NULL)
640
0
    fu_udev_device_set_device_file(uself, fu_udev_device_get_device_file(udonor));
641
0
  if (priv->subsystem == NULL)
642
0
    fu_udev_device_set_subsystem(uself, fu_udev_device_get_subsystem(udonor));
643
0
  if (priv->bind_id == NULL)
644
0
    fu_udev_device_set_bind_id(uself, fu_udev_device_get_bind_id(udonor));
645
0
  if (priv->driver == NULL)
646
0
    fu_udev_device_set_driver(uself, fu_udev_device_get_driver(udonor));
647
0
  if (priv->devtype == NULL)
648
0
    fu_udev_device_set_devtype(uself, fu_udev_device_get_devtype(udonor));
649
0
  if (priv->number == 0x0)
650
0
    fu_udev_device_set_number(uself, fu_udev_device_get_number(udonor));
651
0
  if (priv->open_flags == FU_IO_CHANNEL_OPEN_FLAG_NONE)
652
0
    priv->open_flags = fu_udev_device_get_open_flags(udonor);
653
0
}
654
655
/**
656
 * fu_udev_device_get_subsystem:
657
 * @self: a #FuUdevDevice
658
 *
659
 * Gets the device subsystem, e.g. `pci`
660
 *
661
 * Returns: a subsystem, or NULL if unset or invalid
662
 *
663
 * Since: 1.1.2
664
 **/
665
const gchar *
666
fu_udev_device_get_subsystem(FuUdevDevice *self)
667
0
{
668
0
  FuUdevDevicePrivate *priv = GET_PRIVATE(self);
669
0
  g_return_val_if_fail(FU_IS_UDEV_DEVICE(self), NULL);
670
0
  return priv->subsystem;
671
0
}
672
673
/**
674
 * fu_udev_device_get_bind_id:
675
 * @self: a #FuUdevDevice
676
 *
677
 * Gets the device ID used for binding the device, e.g. `pci:1:2:3`
678
 *
679
 * Returns: a bind_id, or NULL if unset or invalid
680
 *
681
 * Since: 1.7.2
682
 **/
683
const gchar *
684
fu_udev_device_get_bind_id(FuUdevDevice *self)
685
0
{
686
0
  FuUdevDevicePrivate *priv = GET_PRIVATE(self);
687
0
  g_return_val_if_fail(FU_IS_UDEV_DEVICE(self), NULL);
688
0
  fu_udev_device_ensure_bind_id(self, NULL);
689
0
  return priv->bind_id;
690
0
}
691
692
/**
693
 * fu_udev_device_get_driver:
694
 * @self: a #FuUdevDevice
695
 *
696
 * Gets the device driver, e.g. `psmouse`.
697
 *
698
 * Returns: a subsystem, or NULL if unset or invalid
699
 *
700
 * Since: 1.5.3
701
 **/
702
const gchar *
703
fu_udev_device_get_driver(FuUdevDevice *self)
704
0
{
705
0
  FuUdevDevicePrivate *priv = GET_PRIVATE(self);
706
0
  g_return_val_if_fail(FU_IS_UDEV_DEVICE(self), NULL);
707
0
  return priv->driver;
708
0
}
709
710
/**
711
 * fu_udev_device_get_device_file:
712
 * @self: a #FuUdevDevice
713
 *
714
 * Gets the device node.
715
 *
716
 * Returns: a device file, or NULL if unset
717
 *
718
 * Since: 1.3.1
719
 **/
720
const gchar *
721
fu_udev_device_get_device_file(FuUdevDevice *self)
722
0
{
723
0
  FuUdevDevicePrivate *priv = GET_PRIVATE(self);
724
0
  g_return_val_if_fail(FU_IS_UDEV_DEVICE(self), NULL);
725
0
  return priv->device_file;
726
0
}
727
728
/**
729
 * fu_udev_device_get_sysfs_path:
730
 * @self: a #FuUdevDevice
731
 *
732
 * Gets the device sysfs path, e.g. `/sys/devices/pci0000:00/0000:00:14.0`.
733
 *
734
 * Returns: a local path, or NULL if unset or invalid
735
 *
736
 * Since: 1.1.2
737
 **/
738
const gchar *
739
fu_udev_device_get_sysfs_path(FuUdevDevice *self)
740
0
{
741
0
  g_return_val_if_fail(FU_IS_UDEV_DEVICE(self), NULL);
742
0
  return fu_device_get_backend_id(FU_DEVICE(self));
743
0
}
744
745
/**
746
 * fu_udev_device_get_number:
747
 * @self: a #FuUdevDevice
748
 *
749
 * Gets the device number, if any.
750
 *
751
 * Returns: integer, 0 if the data is unavailable.
752
 *
753
 * Since: 1.5.0
754
 **/
755
guint64
756
fu_udev_device_get_number(FuUdevDevice *self)
757
0
{
758
0
  FuUdevDevicePrivate *priv = GET_PRIVATE(self);
759
0
  g_return_val_if_fail(FU_IS_UDEV_DEVICE(self), 0);
760
0
  return priv->number;
761
0
}
762
763
static gchar *
764
fu_udev_device_get_parent_subsystems(FuUdevDevice *self)
765
0
{
766
0
  FuUdevDevicePrivate *priv = GET_PRIVATE(self);
767
0
  g_autoptr(GString) str = g_string_new(NULL);
768
0
  g_autoptr(FuUdevDevice) udev_device = g_object_ref(self);
769
770
  /* not true, but good enough for emulation */
771
0
  if (fu_device_has_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_EMULATED))
772
0
    return g_strdup(priv->subsystem);
773
774
  /* find subsystems of self and all parent devices */
775
0
  while (TRUE) {
776
0
    g_autoptr(FuUdevDevice) parent = NULL;
777
0
    if (fu_udev_device_get_devtype(udev_device) != NULL) {
778
0
      g_string_append_printf(str,
779
0
                 "%s:%s,",
780
0
                 fu_udev_device_get_subsystem(udev_device),
781
0
                 fu_udev_device_get_devtype(udev_device));
782
0
    } else {
783
0
      g_string_append_printf(str,
784
0
                 "%s,",
785
0
                 fu_udev_device_get_subsystem(udev_device));
786
0
    }
787
0
    parent = FU_UDEV_DEVICE(
788
0
        fu_device_get_backend_parent_with_subsystem(FU_DEVICE(udev_device),
789
0
                NULL,
790
0
                NULL));
791
0
    if (parent == NULL)
792
0
      break;
793
0
    g_set_object(&udev_device, parent);
794
0
  }
795
0
  if (str->len > 0)
796
0
    g_string_truncate(str, str->len - 1);
797
0
  return g_string_free(g_steal_pointer(&str), FALSE);
798
0
}
799
800
/* private */
801
gboolean
802
fu_udev_device_match_subsystem(FuUdevDevice *self, const gchar *subsystem)
803
0
{
804
0
  g_auto(GStrv) subsys_devtype = NULL;
805
806
0
  g_return_val_if_fail(FU_IS_UDEV_DEVICE(self), FALSE);
807
808
0
  if (subsystem == NULL)
809
0
    return TRUE;
810
0
  subsys_devtype = g_strsplit(subsystem, ":", 2);
811
0
  if (g_strcmp0(fu_udev_device_get_subsystem(self), subsys_devtype[0]) != 0)
812
0
    return FALSE;
813
0
  if (subsys_devtype[1] != NULL &&
814
0
      g_strcmp0(fu_udev_device_get_devtype(self), subsys_devtype[1]) != 0) {
815
0
    return FALSE;
816
0
  }
817
0
  return TRUE;
818
0
}
819
820
/* private */
821
gchar *
822
fu_udev_device_get_device_file_from_subsystem(FuUdevDevice *self,
823
                const gchar *subsystem,
824
                GError **error)
825
0
{
826
0
  const gchar *fn;
827
0
  g_autofree gchar *subsystem_dir = NULL;
828
0
  g_autoptr(GDir) dir = NULL;
829
0
  g_autoptr(GError) error_local = NULL;
830
831
0
  g_return_val_if_fail(FU_IS_UDEV_DEVICE(self), NULL);
832
0
  g_return_val_if_fail(subsystem != NULL, NULL);
833
0
  g_return_val_if_fail(error == NULL || *error == NULL, NULL);
834
835
0
  subsystem_dir =
836
0
      g_build_filename(fu_udev_device_get_sysfs_path(FU_UDEV_DEVICE(self)), subsystem, NULL);
837
0
  dir = g_dir_open(subsystem_dir, 0, &error_local);
838
0
  if (dir == NULL) {
839
0
    if (g_error_matches(error_local, G_FILE_ERROR_NOENT, G_FILE_ERROR_NOENT)) {
840
0
      g_set_error(error,
841
0
            FWUPD_ERROR,
842
0
            FWUPD_ERROR_NOT_FOUND,
843
0
            "failed to find subsystem directory %s",
844
0
            subsystem_dir);
845
0
      return NULL;
846
0
    }
847
0
    g_propagate_error(error, g_steal_pointer(&error_local));
848
0
    fwupd_error_convert(error);
849
0
    return NULL;
850
0
  }
851
0
  fn = g_dir_read_name(dir);
852
0
  if (fn == NULL) {
853
0
    g_set_error(error,
854
0
          FWUPD_ERROR,
855
0
          FWUPD_ERROR_NOT_FOUND,
856
0
          "failed to find subsystem device in %s",
857
0
          subsystem_dir);
858
0
    return NULL;
859
0
  }
860
0
  return g_strdup_printf("/dev/%s", fn);
861
0
}
862
863
/**
864
 * fu_udev_device_get_devpath:
865
 * @self: a #FuUdevDevice
866
 *
867
 * Sets the physical ID from the sysfs path, with the `/sys` prefixed removed.
868
 *
869
 * Returns: The udev-compatble device path, or %NULL on error.
870
 *
871
 * Since: 2.0.1
872
 **/
873
gchar *
874
fu_udev_device_get_devpath(FuUdevDevice *self)
875
0
{
876
0
  gchar *full_devpath;
877
0
  if (fu_udev_device_get_sysfs_path(self) == NULL)
878
0
    return NULL;
879
0
  full_devpath = g_strrstr(fu_udev_device_get_sysfs_path(self), "/sys");
880
0
  if (full_devpath == NULL)
881
0
    return NULL;
882
0
  return g_strdup(full_devpath + 4);
883
0
}
884
885
/**
886
 * fu_udev_device_set_physical_id:
887
 * @self: a #FuUdevDevice
888
 * @subsystems: a subsystem string, e.g. `pci,usb,scsi:scsi_target`
889
 * @error: (nullable): optional return location for an error
890
 *
891
 * Sets the physical ID from the device subsystem. Plugins should choose the
892
 * subsystem that is "deepest" in the udev tree, for instance choosing `usb`
893
 * over `pci` for a mouse device.
894
 *
895
 * The devtype can also be specified for a specific device, which is useful when the
896
 * subsystem alone is not enough to identify the physical device. e.g. ignoring the
897
 * specific LUNs for a SCSI device.
898
 *
899
 * Returns: %TRUE if the physical device was set.
900
 *
901
 * Since: 1.1.2
902
 **/
903
gboolean
904
fu_udev_device_set_physical_id(FuUdevDevice *self, const gchar *subsystems, GError **error)
905
0
{
906
0
  const gchar *subsystem = NULL;
907
0
  g_autofree gchar *physical_id = NULL;
908
0
  g_auto(GStrv) split = NULL;
909
0
  g_autoptr(FuUdevDevice) udev_device = NULL;
910
911
0
  g_return_val_if_fail(FU_IS_UDEV_DEVICE(self), FALSE);
912
0
  g_return_val_if_fail(subsystems != NULL, FALSE);
913
0
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
914
915
  /* look for each subsystem[:devtype] in turn */
916
0
  split = g_strsplit(subsystems, ",", -1);
917
0
  for (guint i = 0; split[i] != NULL; i++) {
918
0
    g_autoptr(FuUdevDevice) device_parent = NULL;
919
920
    /* do we match */
921
0
    if (fu_udev_device_match_subsystem(self, split[i])) {
922
0
      udev_device = g_object_ref(self);
923
0
      break;
924
0
    }
925
926
    /* does a parent match? */
927
0
    device_parent = FU_UDEV_DEVICE(
928
0
        fu_device_get_backend_parent_with_subsystem(FU_DEVICE(self), split[i], NULL));
929
0
    if (device_parent != NULL) {
930
0
      udev_device = g_object_ref(device_parent);
931
0
      break;
932
0
    }
933
0
  }
934
0
  if (udev_device == NULL) {
935
0
    g_autofree gchar *str = fu_udev_device_get_parent_subsystems(self);
936
0
    g_set_error(error,
937
0
          FWUPD_ERROR,
938
0
          FWUPD_ERROR_NOT_FOUND,
939
0
          "failed to find device with subsystems %s, only got %s",
940
0
          subsystems,
941
0
          str);
942
0
    return FALSE;
943
0
  }
944
945
0
  subsystem = fu_udev_device_get_subsystem(udev_device);
946
0
  if (subsystem == NULL && fu_device_get_physical_id(FU_DEVICE(udev_device)) != NULL) {
947
0
    fu_device_set_physical_id(FU_DEVICE(self),
948
0
            fu_device_get_physical_id(FU_DEVICE(udev_device)));
949
0
    return TRUE;
950
0
  }
951
0
  if (g_strcmp0(subsystem, "pci") == 0) {
952
0
    g_autofree gchar *prop_id =
953
0
        fu_udev_device_read_property(udev_device, "PCI_SLOT_NAME", error);
954
0
    if (prop_id == NULL)
955
0
      return FALSE;
956
0
    physical_id = g_strdup_printf("PCI_SLOT_NAME=%s", prop_id);
957
0
  } else if (g_strcmp0(subsystem, "usb") == 0 || g_strcmp0(subsystem, "mmc") == 0 ||
958
0
       g_strcmp0(subsystem, "i2c") == 0 || g_strcmp0(subsystem, "platform") == 0 ||
959
0
       g_strcmp0(subsystem, "mtd") == 0 || g_strcmp0(subsystem, "block") == 0 ||
960
0
       g_strcmp0(subsystem, "gpio") == 0 || g_strcmp0(subsystem, "video4linux") == 0) {
961
0
    g_auto(GStrv) sysfs_parts =
962
0
        g_strsplit(fu_udev_device_get_sysfs_path(udev_device), "/sys", 2);
963
0
    if (sysfs_parts[1] != NULL)
964
0
      physical_id = g_strdup_printf("DEVPATH=%s", sysfs_parts[1]);
965
0
  } else if (g_strcmp0(subsystem, "hid") == 0) {
966
0
    g_autofree gchar *prop_id =
967
0
        fu_udev_device_read_property(udev_device, "HID_PHYS", error);
968
0
    if (prop_id == NULL)
969
0
      return FALSE;
970
0
    physical_id = g_strdup_printf("HID_PHYS=%s", prop_id);
971
0
  } else if (g_strcmp0(subsystem, "drm_dp_aux_dev") == 0) {
972
0
    g_autofree gchar *prop_id =
973
0
        fu_udev_device_read_property(udev_device, "DEVNAME", error);
974
0
    if (prop_id == NULL)
975
0
      return FALSE;
976
0
    physical_id = g_strdup_printf("DEVNAME=%s", prop_id);
977
0
  } else {
978
0
    g_set_error(error,
979
0
          FWUPD_ERROR,
980
0
          FWUPD_ERROR_NOT_SUPPORTED,
981
0
          "cannot handle subsystem %s",
982
0
          subsystem);
983
0
    return FALSE;
984
0
  }
985
986
  /* success */
987
0
  fu_device_set_physical_id(FU_DEVICE(self), physical_id);
988
0
  return TRUE;
989
0
}
990
991
/**
992
 * fu_udev_device_get_io_channel:
993
 * @self: a #FuUdevDevice
994
 *
995
 * Gets the IO channel.
996
 *
997
 * Returns: (transfer none): a #FuIOChannel, or %NULL if the device is not open
998
 *
999
 * Since: 1.9.8
1000
 **/
1001
FuIOChannel *
1002
fu_udev_device_get_io_channel(FuUdevDevice *self)
1003
0
{
1004
0
  FuUdevDevicePrivate *priv = GET_PRIVATE(self);
1005
0
  g_return_val_if_fail(FU_IS_UDEV_DEVICE(self), NULL);
1006
0
  return priv->io_channel;
1007
0
}
1008
1009
/**
1010
 * fu_udev_device_set_io_channel:
1011
 * @self: a #FuUdevDevice
1012
 * @io_channel: a #FuIOChannel
1013
 *
1014
 * Replace the IO channel to use when the device has already been opened.
1015
 * This object will automatically unref @io_channel when fu_device_close() is called.
1016
 *
1017
 * Since: 1.9.8
1018
 **/
1019
void
1020
fu_udev_device_set_io_channel(FuUdevDevice *self, FuIOChannel *io_channel)
1021
0
{
1022
0
  FuUdevDevicePrivate *priv = GET_PRIVATE(self);
1023
0
  g_return_if_fail(FU_IS_UDEV_DEVICE(self));
1024
0
  g_return_if_fail(FU_IS_IO_CHANNEL(io_channel));
1025
0
  g_set_object(&priv->io_channel, io_channel);
1026
0
}
1027
1028
/**
1029
 * fu_udev_device_remove_open_flag:
1030
 * @self: a #FuUdevDevice
1031
 * @flag: udev device flag, e.g. %FU_IO_CHANNEL_OPEN_FLAG_READ
1032
 *
1033
 * Removes a open flag.
1034
 *
1035
 * Since: 2.0.0
1036
 **/
1037
void
1038
fu_udev_device_remove_open_flag(FuUdevDevice *self, FuIoChannelOpenFlags flag)
1039
0
{
1040
0
  FuUdevDevicePrivate *priv = GET_PRIVATE(self);
1041
0
  g_return_if_fail(FU_IS_UDEV_DEVICE(self));
1042
0
  priv->open_flags &= ~flag;
1043
0
}
1044
1045
/**
1046
 * fu_udev_device_add_open_flag:
1047
 * @self: a #FuUdevDevice
1048
 * @flag: udev device flag, e.g. %FU_IO_CHANNEL_OPEN_FLAG_READ
1049
 *
1050
 * Sets the parameters to use when opening the device.
1051
 *
1052
 * For example %FU_IO_CHANNEL_OPEN_FLAG_READ means that fu_device_open()
1053
 * would use `O_RDONLY` rather than `O_RDWR` which is the default.
1054
 *
1055
 * Since: 2.0.0
1056
 **/
1057
void
1058
fu_udev_device_add_open_flag(FuUdevDevice *self, FuIoChannelOpenFlags flag)
1059
0
{
1060
0
  FuUdevDevicePrivate *priv = GET_PRIVATE(self);
1061
0
  g_return_if_fail(FU_IS_UDEV_DEVICE(self));
1062
1063
  /* already set */
1064
0
  if (priv->open_flags & flag)
1065
0
    return;
1066
0
  priv->open_flags |= flag;
1067
0
}
1068
1069
static gboolean
1070
fu_udev_device_open(FuDevice *device, GError **error)
1071
0
{
1072
0
  FuUdevDevice *self = FU_UDEV_DEVICE(device);
1073
0
  FuUdevDevicePrivate *priv = GET_PRIVATE(self);
1074
1075
  /* emulated */
1076
0
  if (fu_device_has_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_EMULATED))
1077
0
    return TRUE;
1078
1079
  /* old versions of fwupd used to start with OPEN_READ|OPEN_WRITE and then plugins
1080
   * could add more flags, or set the flags back to NONE -- detect and fixup */
1081
0
  if (priv->device_file != NULL && priv->open_flags == FU_IO_CHANNEL_OPEN_FLAG_NONE) {
1082
0
#ifndef SUPPORTED_BUILD
1083
0
    g_critical("%s [%s] forgot to call fu_udev_device_add_open_flag() with "
1084
0
         "FU_IO_CHANNEL_OPEN_FLAG_READ and/or FU_IO_CHANNEL_OPEN_FLAG_WRITE",
1085
0
         fu_device_get_name(device),
1086
0
         fu_device_get_id(device));
1087
0
#endif
1088
0
    fu_udev_device_add_open_flag(FU_UDEV_DEVICE(self), FU_IO_CHANNEL_OPEN_FLAG_READ);
1089
0
    fu_udev_device_add_open_flag(FU_UDEV_DEVICE(self), FU_IO_CHANNEL_OPEN_FLAG_WRITE);
1090
0
  }
1091
1092
  /* open device */
1093
0
  if (priv->device_file != NULL) {
1094
0
    g_autoptr(FuIOChannel) io_channel = NULL;
1095
0
    io_channel = fu_io_channel_new_file(priv->device_file, priv->open_flags, error);
1096
0
    if (io_channel == NULL)
1097
0
      return FALSE;
1098
0
    g_set_object(&priv->io_channel, io_channel);
1099
0
  }
1100
1101
  /* success */
1102
0
  return TRUE;
1103
0
}
1104
1105
static gboolean
1106
fu_udev_device_rescan(FuDevice *device, GError **error)
1107
0
{
1108
0
  if (fu_device_has_flag(device, FWUPD_DEVICE_FLAG_EMULATED))
1109
0
    return TRUE;
1110
0
  fu_device_probe_invalidate(device);
1111
0
  return fu_device_probe(device, error);
1112
0
}
1113
1114
static gboolean
1115
fu_udev_device_close(FuDevice *device, GError **error)
1116
0
{
1117
0
  FuUdevDevice *self = FU_UDEV_DEVICE(device);
1118
0
  FuUdevDevicePrivate *priv = GET_PRIVATE(self);
1119
1120
  /* emulated */
1121
0
  if (fu_device_has_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_EMULATED))
1122
0
    return TRUE;
1123
1124
  /* optional */
1125
0
  if (priv->io_channel != NULL) {
1126
0
    if (!fu_io_channel_shutdown(priv->io_channel, error))
1127
0
      return FALSE;
1128
0
  }
1129
1130
  /* success */
1131
0
  return TRUE;
1132
0
}
1133
1134
/**
1135
 * fu_udev_device_reopen:
1136
 * @self: a #FuDevice
1137
 * @error: (nullable): optional return location for an error
1138
 *
1139
 * Closes and opens the device, typically used to close() and open() the device-file which is
1140
 * required by some ioctls.
1141
 *
1142
 * Returns: %TRUE for success
1143
 *
1144
 * Since: 2.0.9
1145
 **/
1146
gboolean
1147
fu_udev_device_reopen(FuUdevDevice *self, GError **error)
1148
0
{
1149
0
  g_return_val_if_fail(FU_IS_UDEV_DEVICE(self), FALSE);
1150
0
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
1151
1152
0
  if (!fu_udev_device_close(FU_DEVICE(self), error))
1153
0
    return FALSE;
1154
0
  return fu_udev_device_open(FU_DEVICE(self), error);
1155
0
}
1156
1157
/**
1158
 * fu_udev_device_ioctl_new:
1159
 * @self: a #FuUdevDevice
1160
 *
1161
 * Build a helper to control a device using a low-level request.
1162
 *
1163
 * Returns: (transfer full): a #FuIoctl, or %NULL on error
1164
 *
1165
 * Since: 2.0.2
1166
 **/
1167
FuIoctl *
1168
fu_udev_device_ioctl_new(FuUdevDevice *self)
1169
0
{
1170
0
  g_return_val_if_fail(FU_IS_UDEV_DEVICE(self), NULL);
1171
0
  return fu_ioctl_new(self);
1172
0
}
1173
1174
/* private */
1175
gboolean
1176
fu_udev_device_ioctl(FuUdevDevice *self,
1177
         gulong request,
1178
         guint8 *buf,
1179
         gsize bufsz,
1180
         gint *rc,
1181
         guint timeout,
1182
         FuIoctlFlags flags,
1183
         GError **error)
1184
0
{
1185
#ifdef HAVE_IOCTL_H
1186
  FuUdevDevicePrivate *priv = GET_PRIVATE(self);
1187
  gint rc_tmp;
1188
  g_autoptr(GTimer) timer = g_timer_new();
1189
1190
  g_return_val_if_fail(FU_IS_UDEV_DEVICE(self), FALSE);
1191
  g_return_val_if_fail(request != 0x0, FALSE);
1192
  g_return_val_if_fail(buf != NULL, FALSE);
1193
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
1194
1195
  /* not open! */
1196
  if (priv->io_channel == NULL) {
1197
    g_set_error(error,
1198
          FWUPD_ERROR,
1199
          FWUPD_ERROR_INTERNAL,
1200
          "%s [%s] has not been opened",
1201
          fu_device_get_id(FU_DEVICE(self)),
1202
          fu_device_get_name(FU_DEVICE(self)));
1203
    return FALSE;
1204
  }
1205
1206
  /* poll if required  up to the timeout */
1207
  do {
1208
    rc_tmp = ioctl(fu_io_channel_unix_get_fd(priv->io_channel), /* nocheck:blocked */
1209
             request,
1210
             buf);
1211
    if (rc_tmp >= 0)
1212
      break;
1213
  } while ((flags & FU_IOCTL_FLAG_RETRY) && (errno == EINTR || errno == EAGAIN) &&
1214
     g_timer_elapsed(timer, NULL) < timeout * 1000.f);
1215
  if (rc != NULL)
1216
    *rc = rc_tmp;
1217
  if (rc_tmp < 0) {
1218
#ifdef HAVE_ERRNO_H
1219
    if (errno == EPERM) {
1220
      g_set_error_literal(error,
1221
              FWUPD_ERROR,
1222
              FWUPD_ERROR_PERMISSION_DENIED,
1223
              "permission denied");
1224
      return FALSE;
1225
    }
1226
    if (errno == ENOTTY) {
1227
      g_set_error_literal(error,
1228
              FWUPD_ERROR,
1229
              FWUPD_ERROR_NOT_SUPPORTED,
1230
              "permission denied");
1231
      return FALSE;
1232
    }
1233
    g_set_error(error,
1234
          FWUPD_ERROR,
1235
          FWUPD_ERROR_INTERNAL,
1236
          "ioctl error: %s [%i]",
1237
          fwupd_strerror(errno),
1238
          errno);
1239
#else
1240
    g_set_error_literal(error,
1241
            FWUPD_ERROR,
1242
            FWUPD_ERROR_INTERNAL,
1243
            "unspecified ioctl error");
1244
#endif
1245
    return FALSE;
1246
  }
1247
1248
  /* success */
1249
  return TRUE;
1250
#else
1251
0
  g_set_error_literal(error,
1252
0
          FWUPD_ERROR,
1253
0
          FWUPD_ERROR_NOT_SUPPORTED,
1254
0
          "not supported as <sys/ioctl.h> not found");
1255
0
  return FALSE;
1256
0
#endif
1257
0
}
1258
1259
/**
1260
 * fu_udev_device_pread:
1261
 * @self: a #FuUdevDevice
1262
 * @port: offset address
1263
 * @buf: (in): data
1264
 * @bufsz: size of @buf
1265
 * @error: (nullable): optional return location for an error
1266
 *
1267
 * Read a buffer from a file descriptor at a given offset.
1268
 *
1269
 * Returns: %TRUE for success
1270
 *
1271
 * Since: 1.8.2
1272
 **/
1273
gboolean
1274
fu_udev_device_pread(FuUdevDevice *self, goffset port, guint8 *buf, gsize bufsz, GError **error)
1275
0
{
1276
0
  FuUdevDevicePrivate *priv = GET_PRIVATE(self);
1277
0
  FuDeviceEvent *event = NULL;
1278
0
  g_autofree gchar *event_id = NULL;
1279
1280
0
  g_return_val_if_fail(FU_IS_UDEV_DEVICE(self), FALSE);
1281
0
  g_return_val_if_fail(buf != NULL, FALSE);
1282
0
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
1283
1284
  /* need event ID */
1285
0
  if (fu_device_has_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_EMULATED) ||
1286
0
      fu_context_has_flag(fu_device_get_context(FU_DEVICE(self)),
1287
0
        FU_CONTEXT_FLAG_SAVE_EVENTS)) {
1288
0
    event_id = g_strdup_printf("Pread:"
1289
0
             "Port=0x%x,"
1290
0
             "Length=0x%x",
1291
0
             (guint)port,
1292
0
             (guint)bufsz);
1293
0
  }
1294
1295
  /* emulated */
1296
0
  if (fu_device_has_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_EMULATED)) {
1297
0
    event = fu_device_load_event(FU_DEVICE(self), event_id, error);
1298
0
    if (event == NULL)
1299
0
      return FALSE;
1300
0
    return fu_device_event_copy_data(event, "Data", buf, bufsz, NULL, error);
1301
0
  }
1302
1303
  /* save */
1304
0
  if (event_id != NULL)
1305
0
    event = fu_device_save_event(FU_DEVICE(self), event_id);
1306
1307
  /* not open! */
1308
0
  if (priv->io_channel == NULL) {
1309
0
    g_set_error(error,
1310
0
          FWUPD_ERROR,
1311
0
          FWUPD_ERROR_INTERNAL,
1312
0
          "%s [%s] has not been opened",
1313
0
          fu_device_get_id(FU_DEVICE(self)),
1314
0
          fu_device_get_name(FU_DEVICE(self)));
1315
0
    return FALSE;
1316
0
  }
1317
1318
#ifdef HAVE_PWRITE
1319
  if (pread(fu_io_channel_unix_get_fd(priv->io_channel), buf, bufsz, port) != (gssize)bufsz) {
1320
    g_set_error(error,
1321
          G_IO_ERROR, /* nocheck:error */
1322
#ifdef HAVE_ERRNO_H
1323
          g_io_error_from_errno(errno),
1324
#else
1325
          G_IO_ERROR_FAILED, /* nocheck:error */
1326
#endif
1327
          "failed to read from port 0x%04x: %s",
1328
          (guint)port,
1329
          fwupd_strerror(errno));
1330
    fwupd_error_convert(error);
1331
    return FALSE;
1332
  }
1333
1334
  /* save response */
1335
  if (event != NULL)
1336
    fu_device_event_set_data(event, "Data", buf, bufsz);
1337
  return TRUE;
1338
#else
1339
0
  g_set_error_literal(error,
1340
0
          FWUPD_ERROR,
1341
0
          FWUPD_ERROR_NOT_SUPPORTED,
1342
0
          "Not supported as pread() is unavailable");
1343
0
  return FALSE;
1344
0
#endif
1345
0
}
1346
1347
/**
1348
 * fu_udev_device_seek:
1349
 * @self: a #FuUdevDevice
1350
 * @offset: offset address
1351
 * @error: (nullable): optional return location for an error
1352
 *
1353
 * Seeks a file descriptor to a given offset.
1354
 *
1355
 * Returns: %TRUE for success
1356
 *
1357
 * Since: 1.7.2
1358
 **/
1359
gboolean
1360
fu_udev_device_seek(FuUdevDevice *self, goffset offset, GError **error)
1361
0
{
1362
0
  FuUdevDevicePrivate *priv = GET_PRIVATE(self);
1363
1364
0
  g_return_val_if_fail(FU_IS_UDEV_DEVICE(self), FALSE);
1365
0
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
1366
1367
  /* emulated */
1368
0
  if (fu_device_has_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_EMULATED))
1369
0
    return TRUE;
1370
1371
  /* not open! */
1372
0
  if (priv->io_channel == NULL) {
1373
0
    g_set_error(error,
1374
0
          FWUPD_ERROR,
1375
0
          FWUPD_ERROR_INTERNAL,
1376
0
          "%s [%s] has not been opened",
1377
0
          fu_device_get_id(FU_DEVICE(self)),
1378
0
          fu_device_get_name(FU_DEVICE(self)));
1379
0
    return FALSE;
1380
0
  }
1381
0
  return fu_io_channel_seek(priv->io_channel, offset, error);
1382
0
}
1383
1384
/**
1385
 * fu_udev_device_pwrite:
1386
 * @self: a #FuUdevDevice
1387
 * @port: offset address
1388
 * @buf: (out): data
1389
 * @bufsz: size of @data
1390
 * @error: (nullable): optional return location for an error
1391
 *
1392
 * Write a buffer to a file descriptor at a given offset.
1393
 *
1394
 * Returns: %TRUE for success
1395
 *
1396
 * Since: 1.8.2
1397
 **/
1398
gboolean
1399
fu_udev_device_pwrite(FuUdevDevice *self,
1400
          goffset port,
1401
          const guint8 *buf,
1402
          gsize bufsz,
1403
          GError **error)
1404
0
{
1405
0
  FuUdevDevicePrivate *priv = GET_PRIVATE(self);
1406
0
  FuDeviceEvent *event = NULL;
1407
0
  g_autofree gchar *event_id = NULL;
1408
1409
0
  g_return_val_if_fail(FU_IS_UDEV_DEVICE(self), FALSE);
1410
0
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
1411
1412
  /* emulated */
1413
0
  if (fu_device_has_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_EMULATED) &&
1414
0
      !fu_device_check_fwupd_version(FU_DEVICE(self), "2.0.13")) {
1415
0
    return TRUE;
1416
0
  }
1417
1418
  /* need event ID */
1419
0
  if (fu_device_has_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_EMULATED) ||
1420
0
      fu_context_has_flag(fu_device_get_context(FU_DEVICE(self)),
1421
0
        FU_CONTEXT_FLAG_SAVE_EVENTS)) {
1422
0
    g_autofree gchar *data_base64 = g_base64_encode(buf, bufsz);
1423
0
    event_id = g_strdup_printf("Pwrite:"
1424
0
             "Port=0x%x,"
1425
0
             "Data=%s,"
1426
0
             "Length=0x%x",
1427
0
             (guint)port,
1428
0
             data_base64,
1429
0
             (guint)bufsz);
1430
0
  }
1431
1432
  /* emulated */
1433
0
  if (fu_device_has_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_EMULATED)) {
1434
0
    event = fu_device_load_event(FU_DEVICE(self), event_id, error);
1435
0
    if (event == NULL)
1436
0
      return FALSE;
1437
0
    return TRUE;
1438
0
  }
1439
1440
  /* save */
1441
0
  if (event_id != NULL)
1442
0
    event = fu_device_save_event(FU_DEVICE(self), event_id);
1443
1444
  /* not open! */
1445
0
  if (priv->io_channel == NULL) {
1446
0
    g_set_error(error,
1447
0
          FWUPD_ERROR,
1448
0
          FWUPD_ERROR_INTERNAL,
1449
0
          "%s [%s] has not been opened",
1450
0
          fu_device_get_id(FU_DEVICE(self)),
1451
0
          fu_device_get_name(FU_DEVICE(self)));
1452
0
    return FALSE;
1453
0
  }
1454
1455
#ifdef HAVE_PWRITE
1456
  if (pwrite(fu_io_channel_unix_get_fd(priv->io_channel), buf, bufsz, port) !=
1457
      (gssize)bufsz) {
1458
    g_set_error(error,
1459
          G_IO_ERROR, /* nocheck:error */
1460
#ifdef HAVE_ERRNO_H
1461
          g_io_error_from_errno(errno),
1462
#else
1463
          G_IO_ERROR_FAILED, /* nocheck:blocked */
1464
#endif
1465
          "failed to write to port %04x: %s",
1466
          (guint)port,
1467
          fwupd_strerror(errno));
1468
    fwupd_error_convert(error);
1469
    return FALSE;
1470
  }
1471
1472
  /* save response */
1473
  if (event != NULL)
1474
    fu_device_event_set_data(event, "Data", buf, bufsz);
1475
1476
  /* success */
1477
  return TRUE;
1478
#else
1479
0
  g_set_error_literal(error,
1480
0
          FWUPD_ERROR,
1481
0
          FWUPD_ERROR_NOT_SUPPORTED,
1482
0
          "Not supported as pwrite() is unavailable");
1483
0
  return FALSE;
1484
0
#endif
1485
0
}
1486
1487
/**
1488
 * fu_udev_device_read:
1489
 * @self: a #FuUdevDevice
1490
 * @buf: (in): data
1491
 * @bufsz: size of @buf
1492
 * @bytes_read: (out) (nullable): data written to @buf
1493
 * @timeout_ms: timeout in ms
1494
 * @flags: channel flags, e.g. %FU_IO_CHANNEL_FLAG_SINGLE_SHOT
1495
 * @error: (nullable): optional return location for an error
1496
 *
1497
 * Read a buffer from a file descriptor.
1498
 *
1499
 * Returns: %TRUE for success
1500
 *
1501
 * Since: 2.0.4
1502
 **/
1503
gboolean
1504
fu_udev_device_read(FuUdevDevice *self,
1505
        guint8 *buf,
1506
        gsize bufsz,
1507
        gsize *bytes_read,
1508
        guint timeout_ms,
1509
        FuIOChannelFlags flags,
1510
        GError **error)
1511
0
{
1512
0
  FuUdevDevicePrivate *priv = GET_PRIVATE(self);
1513
0
  FuDeviceEvent *event = NULL;
1514
0
  gsize buflen_tmp = 0;
1515
0
  g_autofree gchar *event_id = NULL;
1516
0
  g_autoptr(GError) error_local = NULL;
1517
1518
0
  g_return_val_if_fail(FU_IS_UDEV_DEVICE(self), FALSE);
1519
0
  g_return_val_if_fail(buf != NULL, FALSE);
1520
0
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
1521
1522
  /* need event ID */
1523
0
  if (fu_device_has_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_EMULATED) ||
1524
0
      fu_context_has_flag(fu_device_get_context(FU_DEVICE(self)),
1525
0
        FU_CONTEXT_FLAG_SAVE_EVENTS)) {
1526
0
    event_id = g_strdup_printf("Read:Length=0x%x", (guint)bufsz);
1527
0
  }
1528
1529
  /* emulated */
1530
0
  if (fu_device_has_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_EMULATED)) {
1531
0
    event = fu_device_load_event(FU_DEVICE(self), event_id, error);
1532
0
    if (event == NULL)
1533
0
      return FALSE;
1534
0
    if (!fu_device_event_check_error(event, error))
1535
0
      return FALSE;
1536
0
    return fu_device_event_copy_data(event, "Data", buf, bufsz, bytes_read, error);
1537
0
  }
1538
1539
  /* save */
1540
0
  if (event_id != NULL)
1541
0
    event = fu_device_save_event(FU_DEVICE(self), event_id);
1542
1543
  /* not open! */
1544
0
  if (priv->io_channel == NULL) {
1545
0
    g_set_error(error,
1546
0
          FWUPD_ERROR,
1547
0
          FWUPD_ERROR_INTERNAL,
1548
0
          "%s [%s] has not been opened",
1549
0
          fu_device_get_id(FU_DEVICE(self)),
1550
0
          fu_device_get_name(FU_DEVICE(self)));
1551
0
    return FALSE;
1552
0
  }
1553
0
  if (!fu_io_channel_read_raw(priv->io_channel,
1554
0
            buf,
1555
0
            bufsz,
1556
0
            &buflen_tmp,
1557
0
            timeout_ms,
1558
0
            flags,
1559
0
            &error_local)) {
1560
0
    if (event != NULL)
1561
0
      fu_device_event_set_error(event, error_local);
1562
0
    g_propagate_error(error, g_steal_pointer(&error_local));
1563
0
    return FALSE;
1564
0
  }
1565
0
  if (bytes_read != NULL)
1566
0
    *bytes_read = buflen_tmp;
1567
1568
  /* save response */
1569
0
  if (event != NULL)
1570
0
    fu_device_event_set_data(event, "Data", buf, buflen_tmp);
1571
0
  return TRUE;
1572
0
}
1573
1574
/**
1575
 * fu_udev_device_read_bytes:
1576
 * @self: a #FuUdevDevice
1577
 * @count: bytes to read
1578
 * @timeout_ms: timeout in ms
1579
 * @flags: channel flags, e.g. %FU_IO_CHANNEL_FLAG_SINGLE_SHOT
1580
 * @error: (nullable): optional return location for an error
1581
 *
1582
 * Read a buffer from a file descriptor.
1583
 *
1584
 * Returns: (transfer full): A #GBytes, or %NULL
1585
 *
1586
 * Since: 2.0.7
1587
 **/
1588
GBytes *
1589
fu_udev_device_read_bytes(FuUdevDevice *self,
1590
        gsize count,
1591
        guint timeout_ms,
1592
        FuIOChannelFlags flags,
1593
        GError **error)
1594
0
{
1595
0
  gsize bytes_read = 0;
1596
0
  g_autofree guint8 *buf = NULL;
1597
1598
0
  g_return_val_if_fail(FU_IS_UDEV_DEVICE(self), NULL);
1599
0
  g_return_val_if_fail(count > 0, NULL);
1600
0
  g_return_val_if_fail(error == NULL || *error == NULL, NULL);
1601
1602
0
  buf = g_malloc0(count);
1603
0
  if (!fu_udev_device_read(self, buf, count, &bytes_read, timeout_ms, flags, error))
1604
0
    return NULL;
1605
0
  return g_bytes_new(buf, bytes_read);
1606
0
}
1607
1608
/**
1609
 * fu_udev_device_write:
1610
 * @self: a #FuUdevDevice
1611
 * @buf: (out): data
1612
 * @bufsz: size of @data
1613
 * @timeout_ms: timeout in ms
1614
 * @flags: channel flags, e.g. %FU_IO_CHANNEL_FLAG_SINGLE_SHOT
1615
 * @error: (nullable): optional return location for an error
1616
 *
1617
 * Write a buffer to a file descriptor.
1618
 *
1619
 * Returns: %TRUE for success
1620
 *
1621
 * Since: 2.0.4
1622
 **/
1623
gboolean
1624
fu_udev_device_write(FuUdevDevice *self,
1625
         const guint8 *buf,
1626
         gsize bufsz,
1627
         guint timeout_ms,
1628
         FuIOChannelFlags flags,
1629
         GError **error)
1630
0
{
1631
0
  FuUdevDevicePrivate *priv = GET_PRIVATE(self);
1632
0
  FuDeviceEvent *event = NULL;
1633
0
  g_autofree gchar *event_id = NULL;
1634
1635
0
  g_return_val_if_fail(FU_IS_UDEV_DEVICE(self), FALSE);
1636
0
  g_return_val_if_fail(buf != NULL, FALSE);
1637
0
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
1638
1639
  /* emulated */
1640
0
  if (fu_device_has_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_EMULATED) ||
1641
0
      fu_context_has_flag(fu_device_get_context(FU_DEVICE(self)),
1642
0
        FU_CONTEXT_FLAG_SAVE_EVENTS)) {
1643
0
    g_autofree gchar *data_base64 = g_base64_encode(buf, bufsz);
1644
0
    event_id = g_strdup_printf("Write:Data=%s,Length=0x%x", data_base64, (guint)bufsz);
1645
0
  }
1646
1647
  /* emulated */
1648
0
  if (fu_device_has_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_EMULATED)) {
1649
0
    event = fu_device_load_event(FU_DEVICE(self), event_id, error);
1650
0
    if (event == NULL)
1651
0
      return FALSE;
1652
0
    return event != NULL;
1653
0
  }
1654
1655
  /* save */
1656
0
  if (event_id != NULL)
1657
0
    event = fu_device_save_event(FU_DEVICE(self), event_id);
1658
1659
  /* not open! */
1660
0
  if (priv->io_channel == NULL) {
1661
0
    g_set_error(error,
1662
0
          FWUPD_ERROR,
1663
0
          FWUPD_ERROR_INTERNAL,
1664
0
          "%s [%s] has not been opened",
1665
0
          fu_device_get_id(FU_DEVICE(self)),
1666
0
          fu_device_get_name(FU_DEVICE(self)));
1667
0
    return FALSE;
1668
0
  }
1669
0
  if (!fu_io_channel_write_raw(priv->io_channel, buf, bufsz, timeout_ms, flags, error))
1670
0
    return FALSE;
1671
1672
  /* success */
1673
0
  return TRUE;
1674
0
}
1675
1676
/**
1677
 * fu_udev_device_write_bytes:
1678
 * @self: a #FuUdevDevice
1679
 * @blob: a #GBytes
1680
 * @timeout_ms: timeout in ms
1681
 * @flags: channel flags, e.g. %FU_IO_CHANNEL_FLAG_SINGLE_SHOT
1682
 * @error: (nullable): optional return location for an error
1683
 *
1684
 * Write a buffer to a file descriptor.
1685
 *
1686
 * Returns: %TRUE for success
1687
 *
1688
 * Since: 2.0.7
1689
 **/
1690
gboolean
1691
fu_udev_device_write_bytes(FuUdevDevice *self,
1692
         GBytes *blob,
1693
         guint timeout_ms,
1694
         FuIOChannelFlags flags,
1695
         GError **error)
1696
0
{
1697
0
  g_return_val_if_fail(FU_IS_UDEV_DEVICE(self), FALSE);
1698
0
  g_return_val_if_fail(blob != NULL, FALSE);
1699
0
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
1700
0
  return fu_udev_device_write(self,
1701
0
            g_bytes_get_data(blob, NULL),
1702
0
            g_bytes_get_size(blob),
1703
0
            timeout_ms,
1704
0
            flags,
1705
0
            error);
1706
0
}
1707
1708
/**
1709
 * fu_udev_device_list_sysfs:
1710
 * @self: a #FuUdevDevice
1711
 * @error: (nullable): optional return location for an error
1712
 *
1713
 * Lists all the sysfs attributes.
1714
 *
1715
 * Returns: (transfer container) (element-type utf8): basenames, or %NULL
1716
 *
1717
 * Since: 2.0.9
1718
 **/
1719
GPtrArray *
1720
fu_udev_device_list_sysfs(FuUdevDevice *self, GError **error)
1721
0
{
1722
0
  FuDeviceEvent *event = NULL;
1723
0
  const gchar *basename;
1724
0
  g_autofree gchar *event_id = NULL;
1725
0
  g_autoptr(GDir) dir = NULL;
1726
0
  g_autoptr(GPtrArray) attrs = g_ptr_array_new_with_free_func(g_free);
1727
1728
0
  g_return_val_if_fail(FU_IS_UDEV_DEVICE(self), NULL);
1729
0
  g_return_val_if_fail(error == NULL || *error == NULL, NULL);
1730
1731
  /* need event ID */
1732
0
  if (fu_device_has_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_EMULATED) ||
1733
0
      fu_context_has_flag(fu_device_get_context(FU_DEVICE(self)),
1734
0
        FU_CONTEXT_FLAG_SAVE_EVENTS)) {
1735
0
    event_id = g_strdup("ListAttr");
1736
0
  }
1737
1738
  /* emulated */
1739
0
  if (fu_device_has_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_EMULATED)) {
1740
0
    const gchar *value;
1741
0
    g_auto(GStrv) attrs_strv = NULL;
1742
1743
0
    event = fu_device_load_event(FU_DEVICE(self), event_id, error);
1744
0
    if (event == NULL)
1745
0
      return NULL;
1746
0
    value = fu_device_event_get_str(event, "Data", error);
1747
0
    if (value == NULL)
1748
0
      return NULL;
1749
0
    attrs_strv = g_strsplit(value, "\n", -1);
1750
0
    for (guint i = 0; attrs_strv[i] != NULL; i++)
1751
0
      g_ptr_array_add(attrs, g_strdup(attrs_strv[i]));
1752
0
    return g_steal_pointer(&attrs);
1753
0
  }
1754
1755
  /* save */
1756
0
  if (event_id != NULL)
1757
0
    event = fu_device_save_event(FU_DEVICE(self), event_id);
1758
1759
  /* list the files and directories */
1760
0
  if (fu_udev_device_get_sysfs_path(self) == NULL) {
1761
0
    g_set_error_literal(error,
1762
0
            FWUPD_ERROR,
1763
0
            FWUPD_ERROR_INTERNAL,
1764
0
            "sysfs_path undefined");
1765
0
    return NULL;
1766
0
  }
1767
0
  dir = g_dir_open(fu_udev_device_get_sysfs_path(self), 0, error);
1768
0
  if (dir == NULL) {
1769
0
    fwupd_error_convert(error);
1770
0
    return NULL;
1771
0
  }
1772
0
  while ((basename = g_dir_read_name(dir)) != NULL)
1773
0
    g_ptr_array_add(attrs, g_strdup(basename));
1774
1775
  /* save for emulation */
1776
0
  if (event != NULL) {
1777
0
    g_autofree gchar *value = fu_strjoin("\n", attrs);
1778
0
    fu_device_event_set_str(event, "Data", value);
1779
0
  }
1780
1781
  /* success */
1782
0
  return g_steal_pointer(&attrs);
1783
0
}
1784
1785
/**
1786
 * fu_udev_device_read_sysfs:
1787
 * @self: a #FuUdevDevice
1788
 * @attr: sysfs attribute name
1789
 * @timeout_ms: IO timeout in milliseconds
1790
 * @error: (nullable): optional return location for an error
1791
 *
1792
 * Reads data from a sysfs attribute, removing any newline trailing chars.
1793
 *
1794
 * Returns: (transfer full): string value, or %NULL
1795
 *
1796
 * Since: 2.0.0
1797
 **/
1798
gchar *
1799
fu_udev_device_read_sysfs(FuUdevDevice *self, const gchar *attr, guint timeout_ms, GError **error)
1800
0
{
1801
0
  FuDeviceEvent *event = NULL;
1802
0
  g_autofree gchar *event_id = NULL;
1803
0
  g_autofree gchar *path = NULL;
1804
0
  g_autofree gchar *value = NULL;
1805
0
  g_autoptr(FuIOChannel) io_channel = NULL;
1806
0
  g_autoptr(GByteArray) buf = NULL;
1807
1808
0
  g_return_val_if_fail(FU_IS_UDEV_DEVICE(self), NULL);
1809
0
  g_return_val_if_fail(attr != NULL, NULL);
1810
0
  g_return_val_if_fail(error == NULL || *error == NULL, NULL);
1811
1812
  /* need event ID */
1813
0
  if (fu_device_has_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_EMULATED) ||
1814
0
      fu_context_has_flag(fu_device_get_context(FU_DEVICE(self)),
1815
0
        FU_CONTEXT_FLAG_SAVE_EVENTS)) {
1816
0
    event_id = g_strdup_printf("ReadAttr:Attr=%s", attr);
1817
0
  }
1818
1819
  /* emulated */
1820
0
  if (fu_device_has_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_EMULATED)) {
1821
0
    event = fu_device_load_event(FU_DEVICE(self), event_id, error);
1822
0
    if (event == NULL)
1823
0
      return NULL;
1824
0
    return g_strdup(fu_device_event_get_str(event, "Data", error));
1825
0
  }
1826
1827
  /* save */
1828
0
  if (event_id != NULL)
1829
0
    event = fu_device_save_event(FU_DEVICE(self), event_id);
1830
1831
  /* open the file */
1832
0
  if (fu_udev_device_get_sysfs_path(self) == NULL) {
1833
0
    g_set_error_literal(error,
1834
0
            FWUPD_ERROR,
1835
0
            FWUPD_ERROR_INTERNAL,
1836
0
            "sysfs_path undefined");
1837
0
    return NULL;
1838
0
  }
1839
0
  path = g_build_filename(fu_udev_device_get_sysfs_path(self), attr, NULL);
1840
0
  io_channel = fu_io_channel_new_file(path, FU_IO_CHANNEL_OPEN_FLAG_READ, error);
1841
0
  if (io_channel == NULL)
1842
0
    return NULL;
1843
0
  buf = fu_io_channel_read_byte_array(io_channel,
1844
0
              -1,
1845
0
              timeout_ms,
1846
0
              FU_IO_CHANNEL_FLAG_NONE,
1847
0
              error);
1848
0
  if (buf == NULL)
1849
0
    return NULL;
1850
0
  if (!g_utf8_validate((const gchar *)buf->data, buf->len, NULL)) {
1851
0
    g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_DATA, "non UTF-8 data");
1852
0
    return NULL;
1853
0
  }
1854
1855
  /* save response */
1856
0
  value = g_strndup((const gchar *)buf->data, buf->len);
1857
1858
  /* remove the trailing newline */
1859
0
  if (buf->len > 0) {
1860
0
    if (value[buf->len - 1] == '\n')
1861
0
      value[buf->len - 1] = '\0';
1862
0
  }
1863
1864
  /* save for emulation */
1865
0
  if (event != NULL)
1866
0
    fu_device_event_set_str(event, "Data", value);
1867
1868
  /* success */
1869
0
  return g_steal_pointer(&value);
1870
0
}
1871
1872
/**
1873
 * fu_udev_device_read_sysfs_bytes:
1874
 * @self: a #FuUdevDevice
1875
 * @attr: sysfs attribute name
1876
 * @count: maximum bytes to read, or -1 for no limit
1877
 * @timeout_ms: IO timeout in milliseconds
1878
 * @error: (nullable): optional return location for an error
1879
 *
1880
 * Reads raw data from a sysfs attribute.
1881
 *
1882
 * Returns: (transfer full): string value, or %NULL
1883
 *
1884
 * Since: 2.0.1
1885
 **/
1886
GBytes *
1887
fu_udev_device_read_sysfs_bytes(FuUdevDevice *self,
1888
        const gchar *attr,
1889
        gssize count,
1890
        guint timeout_ms,
1891
        GError **error)
1892
0
{
1893
0
  FuDeviceEvent *event = NULL;
1894
0
  g_autofree gchar *event_id = NULL;
1895
0
  g_autofree gchar *path = NULL;
1896
0
  g_autoptr(FuIOChannel) io_channel = NULL;
1897
0
  g_autoptr(GBytes) blob = NULL;
1898
1899
0
  g_return_val_if_fail(FU_IS_UDEV_DEVICE(self), NULL);
1900
0
  g_return_val_if_fail(attr != NULL, NULL);
1901
0
  g_return_val_if_fail(error == NULL || *error == NULL, NULL);
1902
1903
  /* need event ID */
1904
0
  if (fu_device_has_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_EMULATED) ||
1905
0
      fu_context_has_flag(fu_device_get_context(FU_DEVICE(self)),
1906
0
        FU_CONTEXT_FLAG_SAVE_EVENTS)) {
1907
0
    event_id = g_strdup_printf("ReadAttr:Attr=%s", attr);
1908
0
  }
1909
1910
  /* emulated */
1911
0
  if (fu_device_has_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_EMULATED)) {
1912
0
    event = fu_device_load_event(FU_DEVICE(self), event_id, error);
1913
0
    if (event == NULL)
1914
0
      return NULL;
1915
0
    return fu_device_event_get_bytes(event, "Data", error);
1916
0
  }
1917
1918
  /* save */
1919
0
  if (event_id != NULL)
1920
0
    event = fu_device_save_event(FU_DEVICE(self), event_id);
1921
1922
  /* open the file */
1923
0
  if (fu_udev_device_get_sysfs_path(self) == NULL) {
1924
0
    g_set_error_literal(error,
1925
0
            FWUPD_ERROR,
1926
0
            FWUPD_ERROR_INTERNAL,
1927
0
            "sysfs_path undefined");
1928
0
    return NULL;
1929
0
  }
1930
0
  path = g_build_filename(fu_udev_device_get_sysfs_path(self), attr, NULL);
1931
0
  io_channel = fu_io_channel_new_file(path, FU_IO_CHANNEL_OPEN_FLAG_READ, error);
1932
0
  if (io_channel == NULL)
1933
0
    return NULL;
1934
0
  blob =
1935
0
      fu_io_channel_read_bytes(io_channel, count, timeout_ms, FU_IO_CHANNEL_FLAG_NONE, error);
1936
0
  if (blob == NULL)
1937
0
    return NULL;
1938
1939
  /* save for emulation */
1940
0
  if (event != NULL)
1941
0
    fu_device_event_set_bytes(event, "Data", blob);
1942
1943
  /* success */
1944
0
  return g_steal_pointer(&blob);
1945
0
}
1946
1947
/**
1948
 * fu_udev_device_write_sysfs:
1949
 * @self: a #FuUdevDevice
1950
 * @attr: sysfs attribute name
1951
 * @val: data to write into the attribute
1952
 * @timeout_ms: IO timeout in milliseconds
1953
 * @error: (nullable): optional return location for an error
1954
 *
1955
 * Writes data into a sysfs attribute
1956
 *
1957
 * Returns: %TRUE for success
1958
 *
1959
 * Since: 2.0.0
1960
 **/
1961
gboolean
1962
fu_udev_device_write_sysfs(FuUdevDevice *self,
1963
         const gchar *attr,
1964
         const gchar *val,
1965
         guint timeout_ms,
1966
         GError **error)
1967
0
{
1968
0
  FuDeviceEvent *event = NULL;
1969
0
  g_autofree gchar *event_id = NULL;
1970
0
  g_autofree gchar *path = NULL;
1971
0
  g_autoptr(FuIOChannel) io_channel = NULL;
1972
1973
0
  g_return_val_if_fail(FU_IS_UDEV_DEVICE(self), FALSE);
1974
0
  g_return_val_if_fail(attr != NULL, FALSE);
1975
0
  g_return_val_if_fail(val != NULL, FALSE);
1976
0
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
1977
1978
  /* need event ID */
1979
0
  if (fu_device_has_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_EMULATED) ||
1980
0
      fu_context_has_flag(fu_device_get_context(FU_DEVICE(self)),
1981
0
        FU_CONTEXT_FLAG_SAVE_EVENTS)) {
1982
0
    event_id = g_strdup_printf("WriteAttr:Attr=%s,Data=%s", attr, val);
1983
0
  }
1984
1985
  /* emulated */
1986
0
  if (fu_device_has_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_EMULATED)) {
1987
0
    event = fu_device_load_event(FU_DEVICE(self), event_id, error);
1988
0
    return event != NULL;
1989
0
  }
1990
1991
  /* open the file */
1992
0
  if (fu_udev_device_get_sysfs_path(self) == NULL) {
1993
0
    g_set_error_literal(error,
1994
0
            FWUPD_ERROR,
1995
0
            FWUPD_ERROR_INTERNAL,
1996
0
            "sysfs_path undefined");
1997
0
    return FALSE;
1998
0
  }
1999
0
  path = g_build_filename(fu_udev_device_get_sysfs_path(self), attr, NULL);
2000
0
  io_channel = fu_io_channel_new_file(path, FU_IO_CHANNEL_OPEN_FLAG_WRITE, error);
2001
0
  if (io_channel == NULL)
2002
0
    return FALSE;
2003
2004
  /* save */
2005
0
  if (event_id != NULL)
2006
0
    event = fu_device_save_event(FU_DEVICE(self), event_id);
2007
0
  return fu_io_channel_write_raw(io_channel,
2008
0
               (const guint8 *)val,
2009
0
               strlen(val),
2010
0
               timeout_ms,
2011
0
               FU_IO_CHANNEL_FLAG_NONE,
2012
0
               error);
2013
0
}
2014
2015
/**
2016
 * fu_udev_device_write_sysfs_byte_array:
2017
 * @self: a #FuUdevDevice
2018
 * @attr: sysfs attribute name
2019
 * @buf: data to write into the attribute
2020
 * @timeout_ms: IO timeout in milliseconds
2021
 * @error: (nullable): optional return location for an error
2022
 *
2023
 * Writes raw data into a sysfs attribute.
2024
 *
2025
 * Returns: %TRUE for success
2026
 *
2027
 * Since: 2.0.1
2028
 **/
2029
gboolean
2030
fu_udev_device_write_sysfs_byte_array(FuUdevDevice *self,
2031
              const gchar *attr,
2032
              GByteArray *buf,
2033
              guint timeout_ms,
2034
              GError **error)
2035
0
{
2036
0
  FuDeviceEvent *event = NULL;
2037
0
  g_autofree gchar *event_id = NULL;
2038
0
  g_autofree gchar *path = NULL;
2039
0
  g_autoptr(FuIOChannel) io_channel = NULL;
2040
2041
0
  g_return_val_if_fail(FU_IS_UDEV_DEVICE(self), FALSE);
2042
0
  g_return_val_if_fail(attr != NULL, FALSE);
2043
0
  g_return_val_if_fail(buf != NULL, FALSE);
2044
0
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
2045
2046
  /* need event ID */
2047
0
  if (fu_device_has_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_EMULATED) ||
2048
0
      fu_context_has_flag(fu_device_get_context(FU_DEVICE(self)),
2049
0
        FU_CONTEXT_FLAG_SAVE_EVENTS)) {
2050
0
    g_autofree gchar *buf_base64 = g_base64_encode(buf->data, buf->len);
2051
0
    event_id = g_strdup_printf("WriteAttr:Attr=%s,Data=%s", attr, buf_base64);
2052
0
  }
2053
2054
  /* emulated */
2055
0
  if (fu_device_has_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_EMULATED)) {
2056
0
    event = fu_device_load_event(FU_DEVICE(self), event_id, error);
2057
0
    return event != NULL;
2058
0
  }
2059
2060
  /* open the file */
2061
0
  if (fu_udev_device_get_sysfs_path(self) == NULL) {
2062
0
    g_set_error_literal(error,
2063
0
            FWUPD_ERROR,
2064
0
            FWUPD_ERROR_INTERNAL,
2065
0
            "sysfs_path undefined");
2066
0
    return FALSE;
2067
0
  }
2068
0
  path = g_build_filename(fu_udev_device_get_sysfs_path(self), attr, NULL);
2069
0
  io_channel = fu_io_channel_new_file(path, FU_IO_CHANNEL_OPEN_FLAG_WRITE, error);
2070
0
  if (io_channel == NULL)
2071
0
    return FALSE;
2072
2073
  /* save */
2074
0
  if (event_id != NULL)
2075
0
    event = fu_device_save_event(FU_DEVICE(self), event_id);
2076
0
  return fu_io_channel_write_byte_array(io_channel,
2077
0
                buf,
2078
0
                timeout_ms,
2079
0
                FU_IO_CHANNEL_FLAG_NONE,
2080
0
                error);
2081
0
}
2082
2083
/**
2084
 * fu_udev_device_write_sysfs_bytes:
2085
 * @self: a #FuUdevDevice
2086
 * @attr: sysfs attribute name
2087
 * @blob: data to write into the attribute
2088
 * @timeout_ms: IO timeout in milliseconds
2089
 * @error: (nullable): optional return location for an error
2090
 *
2091
 * Writes raw data into a sysfs attribute.
2092
 *
2093
 * Returns: %TRUE for success
2094
 *
2095
 * Since: 2.0.1
2096
 **/
2097
gboolean
2098
fu_udev_device_write_sysfs_bytes(FuUdevDevice *self,
2099
         const gchar *attr,
2100
         GBytes *blob,
2101
         guint timeout_ms,
2102
         GError **error)
2103
0
{
2104
0
  FuDeviceEvent *event = NULL;
2105
0
  g_autofree gchar *event_id = NULL;
2106
0
  g_autofree gchar *path = NULL;
2107
0
  g_autoptr(FuIOChannel) io_channel = NULL;
2108
2109
0
  g_return_val_if_fail(FU_IS_UDEV_DEVICE(self), FALSE);
2110
0
  g_return_val_if_fail(attr != NULL, FALSE);
2111
0
  g_return_val_if_fail(blob != NULL, FALSE);
2112
0
  g_return_val_if_fail(error == NULL || *error == NULL, FALSE);
2113
2114
  /* need event ID */
2115
0
  if (fu_device_has_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_EMULATED) ||
2116
0
      fu_context_has_flag(fu_device_get_context(FU_DEVICE(self)),
2117
0
        FU_CONTEXT_FLAG_SAVE_EVENTS)) {
2118
0
    g_autofree gchar *buf_base64 =
2119
0
        g_base64_encode(g_bytes_get_data(blob, NULL), g_bytes_get_size(blob));
2120
0
    event_id = g_strdup_printf("WriteAttr:Attr=%s,Data=%s", attr, buf_base64);
2121
0
  }
2122
2123
  /* emulated */
2124
0
  if (fu_device_has_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_EMULATED)) {
2125
0
    event = fu_device_load_event(FU_DEVICE(self), event_id, error);
2126
0
    return event != NULL;
2127
0
  }
2128
2129
  /* open the file */
2130
0
  if (fu_udev_device_get_sysfs_path(self) == NULL) {
2131
0
    g_set_error_literal(error,
2132
0
            FWUPD_ERROR,
2133
0
            FWUPD_ERROR_INTERNAL,
2134
0
            "sysfs_path undefined");
2135
0
    return FALSE;
2136
0
  }
2137
0
  path = g_build_filename(fu_udev_device_get_sysfs_path(self), attr, NULL);
2138
0
  io_channel = fu_io_channel_new_file(path, FU_IO_CHANNEL_OPEN_FLAG_WRITE, error);
2139
0
  if (io_channel == NULL)
2140
0
    return FALSE;
2141
2142
  /* save */
2143
0
  if (event_id != NULL)
2144
0
    event = fu_device_save_event(FU_DEVICE(self), event_id);
2145
0
  return fu_io_channel_write_bytes(io_channel,
2146
0
           blob,
2147
0
           timeout_ms,
2148
0
           FU_IO_CHANNEL_FLAG_NONE,
2149
0
           error);
2150
0
}
2151
2152
/**
2153
 * fu_udev_device_get_devtype:
2154
 * @self: a #FuUdevDevice
2155
 *
2156
 * Returns the Udev device type
2157
 *
2158
 * Returns: device type specified in the uevent
2159
 *
2160
 * Since: 1.4.5
2161
 **/
2162
const gchar *
2163
fu_udev_device_get_devtype(FuUdevDevice *self)
2164
0
{
2165
0
  FuUdevDevicePrivate *priv = GET_PRIVATE(self);
2166
0
  g_return_val_if_fail(FU_IS_UDEV_DEVICE(self), NULL);
2167
0
  return priv->devtype;
2168
0
}
2169
2170
/**
2171
 * fu_udev_device_get_subsystem_devtype:
2172
 * @self: a #FuUdevDevice
2173
 *
2174
 * Returns the Udev subsystem and device type, as a string.
2175
 *
2176
 * Returns: (transfer full): `subsystem:devtype`, or just `subsystem` if the latter is unset
2177
 *
2178
 * Since: 2.0.2
2179
 **/
2180
gchar *
2181
fu_udev_device_get_subsystem_devtype(FuUdevDevice *self)
2182
0
{
2183
0
  FuUdevDevicePrivate *priv = GET_PRIVATE(self);
2184
0
  g_return_val_if_fail(FU_IS_UDEV_DEVICE(self), NULL);
2185
0
  if (priv->devtype != NULL)
2186
0
    return g_strdup_printf("%s:%s", priv->subsystem, priv->devtype);
2187
0
  return g_strdup(priv->subsystem);
2188
0
}
2189
2190
/* private */
2191
void
2192
fu_udev_device_add_property(FuUdevDevice *self, const gchar *key, const gchar *value)
2193
0
{
2194
0
  FuUdevDevicePrivate *priv = GET_PRIVATE(self);
2195
0
  g_return_if_fail(FU_IS_UDEV_DEVICE(self));
2196
0
  g_return_if_fail(key != NULL);
2197
2198
  /* this are explicit properties too */
2199
0
  if (g_strcmp0(key, "DEVNAME") == 0)
2200
0
    fu_udev_device_set_device_file(self, value);
2201
0
  if (g_strcmp0(key, "DEVTYPE") == 0)
2202
0
    fu_udev_device_set_devtype(self, value);
2203
2204
0
  g_hash_table_insert(priv->properties, g_strdup(key), g_strdup(value));
2205
0
}
2206
2207
/**
2208
 * fu_udev_device_read_property:
2209
 * @self: a #FuUdevDevice
2210
 * @key: uevent key name, e.g. `HID_PHYS`
2211
 * @error: (nullable): optional return location for an error
2212
 *
2213
 * Gets a value from the `uevent` file.
2214
 *
2215
 * Returns: %TRUE for success
2216
 *
2217
 * Since: 2.0.0
2218
 **/
2219
gchar *
2220
fu_udev_device_read_property(FuUdevDevice *self, const gchar *key, GError **error)
2221
0
{
2222
0
  FuUdevDevicePrivate *priv = GET_PRIVATE(self);
2223
0
  FuDeviceEvent *event = NULL;
2224
0
  g_autofree gchar *event_id = NULL;
2225
0
  g_autofree gchar *value = NULL;
2226
2227
0
  g_return_val_if_fail(FU_IS_UDEV_DEVICE(self), NULL);
2228
0
  g_return_val_if_fail(key != NULL, NULL);
2229
0
  g_return_val_if_fail(error == NULL || *error == NULL, NULL);
2230
2231
  /* need event ID */
2232
0
  if (fu_device_has_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_EMULATED) ||
2233
0
      fu_context_has_flag(fu_device_get_context(FU_DEVICE(self)),
2234
0
        FU_CONTEXT_FLAG_SAVE_EVENTS)) {
2235
0
    event_id = g_strdup_printf("ReadProp:Key=%s", key);
2236
0
  }
2237
2238
  /* emulated */
2239
0
  if (fu_device_has_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_EMULATED)) {
2240
0
    event = fu_device_load_event(FU_DEVICE(self), event_id, error);
2241
0
    if (event == NULL)
2242
0
      return NULL;
2243
0
    return g_strdup(fu_device_event_get_str(event, "Data", error));
2244
0
  }
2245
2246
  /* save */
2247
0
  if (event_id != NULL)
2248
0
    event = fu_device_save_event(FU_DEVICE(self), event_id);
2249
2250
  /* parse key */
2251
0
  if (!priv->properties_valid) {
2252
0
    g_autofree gchar *str = NULL;
2253
0
    g_auto(GStrv) uevent_lines = NULL;
2254
0
    str = fu_udev_device_read_sysfs(self,
2255
0
            "uevent",
2256
0
            FU_UDEV_DEVICE_ATTR_READ_TIMEOUT_DEFAULT,
2257
0
            error);
2258
0
    if (str == NULL)
2259
0
      return NULL;
2260
0
    uevent_lines = g_strsplit(str, "\n", -1);
2261
0
    for (guint i = 0; uevent_lines[i] != NULL; i++) {
2262
      /* only split KEY=VALUE */
2263
0
      if (g_strstr_len(uevent_lines[i], -1, "=") != NULL) {
2264
0
        g_autofree gchar **kvs = g_strsplit(uevent_lines[i], "=", 2);
2265
0
        g_hash_table_insert(priv->properties,
2266
0
                g_steal_pointer(&kvs[0]),
2267
0
                g_steal_pointer(&kvs[1]));
2268
0
      }
2269
0
    }
2270
0
    priv->properties_valid = TRUE;
2271
0
  }
2272
0
  value = g_strdup(g_hash_table_lookup(priv->properties, key));
2273
0
  if (value == NULL) {
2274
0
    g_set_error(error,
2275
0
          FWUPD_ERROR,
2276
0
          FWUPD_ERROR_NOT_FOUND,
2277
0
          "property key %s was not found",
2278
0
          key);
2279
0
    return NULL;
2280
0
  }
2281
2282
  /* save response */
2283
0
  if (event != NULL)
2284
0
    fu_device_event_set_str(event, "Data", value);
2285
2286
  /* success */
2287
0
  return g_steal_pointer(&value);
2288
0
}
2289
2290
static void
2291
fu_udev_device_add_json(FuDevice *device, JsonBuilder *builder, FwupdCodecFlags flags)
2292
0
{
2293
0
  FuUdevDevice *self = FU_UDEV_DEVICE(device);
2294
0
  FuUdevDevicePrivate *priv = GET_PRIVATE(self);
2295
0
  GPtrArray *events = fu_device_get_events(device);
2296
2297
  /* optional properties */
2298
0
  fwupd_codec_json_append(builder, "GType", "FuUdevDevice");
2299
0
  if (fu_udev_device_get_sysfs_path(self) != NULL)
2300
0
    fwupd_codec_json_append(builder, "BackendId", fu_udev_device_get_sysfs_path(self));
2301
0
  if (priv->device_file != NULL)
2302
0
    fwupd_codec_json_append(builder, "DeviceFile", priv->device_file);
2303
0
  if (priv->subsystem != NULL)
2304
0
    fwupd_codec_json_append(builder, "Subsystem", priv->subsystem);
2305
0
  if (priv->devtype != NULL)
2306
0
    fwupd_codec_json_append(builder, "Devtype", priv->devtype);
2307
0
  if (priv->driver != NULL)
2308
0
    fwupd_codec_json_append(builder, "Driver", priv->driver);
2309
0
  if (priv->bind_id != NULL)
2310
0
    fwupd_codec_json_append(builder, "BindId", priv->bind_id);
2311
0
  if (fu_device_get_vid(device) != 0)
2312
0
    fwupd_codec_json_append_int(builder, "Vendor", fu_device_get_vid(device));
2313
0
  if (fu_device_get_pid(device) != 0)
2314
0
    fwupd_codec_json_append_int(builder, "Model", fu_device_get_pid(device));
2315
2316
  /* events */
2317
0
  if (events->len > 0) {
2318
0
    json_builder_set_member_name(builder, "Events");
2319
0
    json_builder_begin_array(builder);
2320
0
    for (guint i = 0; i < events->len; i++) {
2321
0
      FuDeviceEvent *event = g_ptr_array_index(events, i);
2322
0
      json_builder_begin_object(builder);
2323
0
      fwupd_codec_to_json(FWUPD_CODEC(event),
2324
0
              builder,
2325
0
              events->len > 1000 ? flags | FWUPD_CODEC_FLAG_COMPRESSED
2326
0
                     : flags);
2327
0
      json_builder_end_object(builder);
2328
0
    }
2329
0
    json_builder_end_array(builder);
2330
0
  }
2331
0
}
2332
2333
static gboolean
2334
fu_udev_device_from_json(FuDevice *device, JsonObject *json_object, GError **error)
2335
0
{
2336
0
  FuUdevDevice *self = FU_UDEV_DEVICE(device);
2337
0
  const gchar *tmp;
2338
0
  gint64 tmp64;
2339
2340
0
  tmp = json_object_get_string_member_with_default(json_object, "BackendId", NULL);
2341
0
  if (tmp != NULL)
2342
0
    fu_device_set_backend_id(device, tmp);
2343
0
  tmp = json_object_get_string_member_with_default(json_object, "Subsystem", NULL);
2344
0
  if (tmp != NULL)
2345
0
    fu_udev_device_set_subsystem(self, tmp);
2346
0
  tmp = json_object_get_string_member_with_default(json_object, "Devtype", NULL);
2347
0
  if (tmp != NULL)
2348
0
    fu_udev_device_set_devtype(self, tmp);
2349
0
  tmp = json_object_get_string_member_with_default(json_object, "Driver", NULL);
2350
0
  if (tmp != NULL)
2351
0
    fu_udev_device_set_driver(self, tmp);
2352
0
  tmp = json_object_get_string_member_with_default(json_object, "BindId", NULL);
2353
0
  if (tmp != NULL)
2354
0
    fu_udev_device_set_bind_id(self, tmp);
2355
0
  tmp = json_object_get_string_member_with_default(json_object, "DeviceFile", NULL);
2356
0
  if (tmp != NULL)
2357
0
    fu_udev_device_set_device_file(self, tmp);
2358
0
  tmp64 = json_object_get_int_member_with_default(json_object, "Vendor", 0);
2359
0
  if (tmp64 != 0)
2360
0
    fu_device_set_vid(device, tmp64);
2361
0
  tmp64 = json_object_get_int_member_with_default(json_object, "Model", 0);
2362
0
  if (tmp64 != 0)
2363
0
    fu_device_set_pid(device, tmp64);
2364
2365
  /* array of events */
2366
0
  if (json_object_has_member(json_object, "Events")) {
2367
0
    JsonArray *json_array = json_object_get_array_member(json_object, "Events");
2368
0
    for (guint i = 0; i < json_array_get_length(json_array); i++) {
2369
0
      JsonNode *node_tmp = json_array_get_element(json_array, i);
2370
0
      g_autoptr(FuDeviceEvent) event = fu_device_event_new(NULL);
2371
0
      if (!fwupd_codec_from_json(FWUPD_CODEC(event), node_tmp, error))
2372
0
        return FALSE;
2373
0
      fu_device_add_event(device, event);
2374
0
    }
2375
0
  }
2376
2377
  /* success */
2378
0
  return TRUE;
2379
0
}
2380
2381
static void
2382
fu_udev_device_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
2383
0
{
2384
0
  FuUdevDevice *self = FU_UDEV_DEVICE(object);
2385
0
  FuUdevDevicePrivate *priv = GET_PRIVATE(self);
2386
0
  switch (prop_id) {
2387
0
  case PROP_SUBSYSTEM:
2388
0
    g_value_set_string(value, priv->subsystem);
2389
0
    break;
2390
0
  case PROP_BIND_ID:
2391
0
    g_value_set_string(value, priv->bind_id);
2392
0
    break;
2393
0
  case PROP_DRIVER:
2394
0
    g_value_set_string(value, priv->driver);
2395
0
    break;
2396
0
  case PROP_DEVICE_FILE:
2397
0
    g_value_set_string(value, priv->device_file);
2398
0
    break;
2399
0
  case PROP_DEVTYPE:
2400
0
    g_value_set_string(value, priv->devtype);
2401
0
    break;
2402
0
  default:
2403
0
    G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
2404
0
    break;
2405
0
  }
2406
0
}
2407
2408
static void
2409
fu_udev_device_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
2410
0
{
2411
0
  FuUdevDevice *self = FU_UDEV_DEVICE(object);
2412
0
  switch (prop_id) {
2413
0
  case PROP_SUBSYSTEM:
2414
0
    fu_udev_device_set_subsystem(self, g_value_get_string(value));
2415
0
    break;
2416
0
  case PROP_BIND_ID:
2417
0
    fu_udev_device_set_bind_id(self, g_value_get_string(value));
2418
0
    break;
2419
0
  case PROP_DRIVER:
2420
0
    fu_udev_device_set_driver(self, g_value_get_string(value));
2421
0
    break;
2422
0
  case PROP_DEVICE_FILE:
2423
0
    fu_udev_device_set_device_file(self, g_value_get_string(value));
2424
0
    break;
2425
0
  case PROP_DEVTYPE:
2426
0
    fu_udev_device_set_devtype(self, g_value_get_string(value));
2427
0
    break;
2428
0
  default:
2429
0
    G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
2430
0
    break;
2431
0
  }
2432
0
}
2433
2434
static void
2435
fu_udev_device_finalize(GObject *object)
2436
0
{
2437
0
  FuUdevDevice *self = FU_UDEV_DEVICE(object);
2438
0
  FuUdevDevicePrivate *priv = GET_PRIVATE(self);
2439
2440
0
  g_hash_table_unref(priv->properties);
2441
0
  g_free(priv->subsystem);
2442
0
  g_free(priv->devtype);
2443
0
  g_free(priv->bind_id);
2444
0
  g_free(priv->driver);
2445
0
  g_free(priv->device_file);
2446
0
  if (priv->io_channel != NULL)
2447
0
    g_object_unref(priv->io_channel);
2448
2449
0
  G_OBJECT_CLASS(fu_udev_device_parent_class)->finalize(object);
2450
0
}
2451
2452
static void
2453
fu_udev_device_vid_notify_cb(FuDevice *device, GParamSpec *pspec, gpointer user_data)
2454
0
{
2455
0
  fu_device_add_instance_u16(device, "VEN", fu_device_get_vid(device));
2456
0
}
2457
2458
static void
2459
fu_udev_device_pid_notify_cb(FuDevice *device, GParamSpec *pspec, gpointer user_data)
2460
0
{
2461
0
  fu_device_add_instance_u16(device, "DEV", fu_device_get_pid(device));
2462
0
}
2463
2464
static void
2465
fu_udev_device_init(FuUdevDevice *self)
2466
0
{
2467
0
  FuUdevDevicePrivate *priv = GET_PRIVATE(self);
2468
0
  priv->properties = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
2469
0
  fu_device_set_acquiesce_delay(FU_DEVICE(self), 2500);
2470
0
  fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_CAN_EMULATION_TAG);
2471
0
  g_signal_connect(FU_DEVICE(self),
2472
0
       "notify::vid",
2473
0
       G_CALLBACK(fu_udev_device_vid_notify_cb),
2474
0
       NULL);
2475
0
  g_signal_connect(FU_DEVICE(self),
2476
0
       "notify::pid",
2477
0
       G_CALLBACK(fu_udev_device_pid_notify_cb),
2478
0
       NULL);
2479
0
}
2480
2481
static void
2482
fu_udev_device_class_init(FuUdevDeviceClass *klass)
2483
0
{
2484
0
  FuDeviceClass *device_class = FU_DEVICE_CLASS(klass);
2485
0
  GObjectClass *object_class = G_OBJECT_CLASS(klass);
2486
0
  GParamSpec *pspec;
2487
2488
0
  object_class->finalize = fu_udev_device_finalize;
2489
0
  object_class->get_property = fu_udev_device_get_property;
2490
0
  object_class->set_property = fu_udev_device_set_property;
2491
0
  device_class->probe = fu_udev_device_probe;
2492
0
  device_class->rescan = fu_udev_device_rescan;
2493
0
  device_class->incorporate = fu_udev_device_incorporate;
2494
0
  device_class->invalidate = fu_udev_device_invalidate;
2495
0
  device_class->open = fu_udev_device_open;
2496
0
  device_class->close = fu_udev_device_close;
2497
0
  device_class->to_string = fu_udev_device_to_string;
2498
0
  device_class->bind_driver = fu_udev_device_bind_driver;
2499
0
  device_class->unbind_driver = fu_udev_device_unbind_driver;
2500
0
  device_class->probe_complete = fu_udev_device_probe_complete;
2501
0
  device_class->from_json = fu_udev_device_from_json;
2502
0
  device_class->add_json = fu_udev_device_add_json;
2503
2504
  /**
2505
   * FuUdevDevice::changed:
2506
   * @self: the #FuUdevDevice instance that emitted the signal
2507
   *
2508
   * The ::changed signal is emitted when the low-level GUdevDevice has changed.
2509
   *
2510
   * Since: 1.1.2
2511
   **/
2512
0
  signals[SIGNAL_CHANGED] = g_signal_new("changed",
2513
0
                 G_TYPE_FROM_CLASS(object_class),
2514
0
                 G_SIGNAL_RUN_LAST,
2515
0
                 0,
2516
0
                 NULL,
2517
0
                 NULL,
2518
0
                 g_cclosure_marshal_VOID__VOID,
2519
0
                 G_TYPE_NONE,
2520
0
                 0);
2521
2522
  /**
2523
   * FuUdevDevice:subsystem:
2524
   *
2525
   * The device subsystem.
2526
   *
2527
   * Since: 1.1.2
2528
   */
2529
0
  pspec = g_param_spec_string("subsystem",
2530
0
            NULL,
2531
0
            NULL,
2532
0
            NULL,
2533
0
            G_PARAM_READWRITE | G_PARAM_STATIC_NAME);
2534
0
  g_object_class_install_property(object_class, PROP_SUBSYSTEM, pspec);
2535
2536
  /**
2537
   * FuUdevDevice:bind-id:
2538
   *
2539
   * The bind ID to use when binding a new driver.
2540
   *
2541
   * Since: 1.7.2
2542
   */
2543
0
  pspec = g_param_spec_string("bind-id",
2544
0
            NULL,
2545
0
            NULL,
2546
0
            NULL,
2547
0
            G_PARAM_READWRITE | G_PARAM_STATIC_NAME);
2548
0
  g_object_class_install_property(object_class, PROP_BIND_ID, pspec);
2549
2550
  /**
2551
   * FuUdevDevice:driver:
2552
   *
2553
   * The driver being used for the device.
2554
   *
2555
   * Since: 1.5.3
2556
   */
2557
0
  pspec = g_param_spec_string("driver",
2558
0
            NULL,
2559
0
            NULL,
2560
0
            NULL,
2561
0
            G_PARAM_READWRITE | G_PARAM_STATIC_NAME);
2562
0
  g_object_class_install_property(object_class, PROP_DRIVER, pspec);
2563
2564
  /**
2565
   * FuUdevDevice:device-file:
2566
   *
2567
   * The low level file to use for device access.
2568
   *
2569
   * Since: 1.3.1
2570
   */
2571
0
  pspec = g_param_spec_string("device-file",
2572
0
            NULL,
2573
0
            NULL,
2574
0
            NULL,
2575
0
            G_PARAM_READWRITE | G_PARAM_STATIC_NAME);
2576
0
  g_object_class_install_property(object_class, PROP_DEVICE_FILE, pspec);
2577
2578
  /**
2579
   * FuUdevDevice:devtype:
2580
   *
2581
   * The device type.
2582
   *
2583
   * Since: 2.0.0
2584
   */
2585
0
  pspec = g_param_spec_string("devtype",
2586
0
            NULL,
2587
0
            NULL,
2588
0
            NULL,
2589
0
            G_PARAM_READWRITE | G_PARAM_STATIC_NAME);
2590
0
  g_object_class_install_property(object_class, PROP_DEVTYPE, pspec);
2591
0
}
2592
2593
/**
2594
 * fu_udev_device_new:
2595
 * @ctx: (nullable): a #FuContext
2596
 * @sysfs_path: a sysfs path
2597
 *
2598
 * Creates a new #FuUdevDevice.
2599
 *
2600
 * Returns: (transfer full): a #FuUdevDevice
2601
 *
2602
 * Since: 2.0.0
2603
 **/
2604
FuUdevDevice *
2605
fu_udev_device_new(FuContext *ctx, const gchar *sysfs_path)
2606
0
{
2607
0
  return g_object_new(FU_TYPE_UDEV_DEVICE, "context", ctx, "backend-id", sysfs_path, NULL);
2608
0
}