Coverage Report

Created: 2026-04-28 06:49

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/fwupd/libfwupdplugin/fu-usb-bos-descriptor.c
Line
Count
Source
1
/*
2
 * Copyright 2022 Richard Hughes <richard@hughsie.com>
3
 *
4
 * SPDX-License-Identifier: LGPL-2.1-or-later
5
 */
6
7
/**
8
 * FuUsbBosDescriptor:
9
 *
10
 * This object is a thin glib wrapper around a `libusb_bos_dev_capability_descriptor`.
11
 *
12
 * All the data is copied when the object is created and the original descriptor can be destroyed
13
 * at any point.
14
 */
15
16
#include "config.h"
17
18
#include <string.h>
19
20
#include "fu-byte-array.h"
21
#include "fu-common.h"
22
#include "fu-partial-input-stream.h"
23
#include "fu-usb-bos-descriptor-private.h"
24
25
struct _FuUsbBosDescriptor {
26
  FuUsbDescriptor parent_instance;
27
  guint8 length;
28
  guint8 dev_capability_type;
29
};
30
31
static void
32
fu_usb_bos_descriptor_codec_iface_init(FwupdCodecInterface *iface);
33
34
0
G_DEFINE_TYPE_EXTENDED(FuUsbBosDescriptor,
35
0
           fu_usb_bos_descriptor,
36
0
           FU_TYPE_USB_DESCRIPTOR,
37
0
           0,
38
0
           G_IMPLEMENT_INTERFACE(FWUPD_TYPE_CODEC,
39
0
               fu_usb_bos_descriptor_codec_iface_init));
40
0
41
0
static void
42
0
fu_usb_bos_descriptor_export(FuFirmware *firmware, FuFirmwareExportFlags flags, XbBuilderNode *bn)
43
0
{
44
0
  FuUsbBosDescriptor *self = FU_USB_BOS_DESCRIPTOR(firmware);
45
0
  fu_xmlb_builder_insert_kv(bn,
46
0
          "dev_capability_type",
47
0
          fu_usb_descriptor_kind_to_string(self->dev_capability_type));
48
0
}
49
50
static gboolean
51
fu_usb_bos_descriptor_build(FuFirmware *firmware, XbNode *n, GError **error)
52
0
{
53
0
  FuUsbBosDescriptor *self = FU_USB_BOS_DESCRIPTOR(firmware);
54
0
  const gchar *str;
55
56
  /* simple properties */
57
0
  str = xb_node_query_text(n, "dev_capability_type", NULL);
58
0
  if (str != NULL) {
59
0
    self->dev_capability_type = fu_usb_descriptor_kind_from_string(str);
60
0
    if (self->dev_capability_type == FU_USB_DESCRIPTOR_KIND_INVALID) {
61
0
      g_set_error(error,
62
0
            FWUPD_ERROR,
63
0
            FWUPD_ERROR_INVALID_DATA,
64
0
            "invalid dev_capability_type %s",
65
0
            str);
66
0
      return FALSE;
67
0
    }
68
0
  }
69
70
  /* success */
71
0
  return TRUE;
72
0
}
73
74
static gboolean
75
fu_usb_bos_descriptor_from_json(FwupdCodec *codec, FwupdJsonObject *json_obj, GError **error)
76
0
{
77
0
  FuUsbBosDescriptor *self = FU_USB_BOS_DESCRIPTOR(codec);
78
0
  const gchar *str;
79
0
  gint64 tmpi = 0;
80
81
  /* optional properties */
82
0
  if (!fwupd_json_object_get_integer_with_default(json_obj,
83
0
              "DevCapabilityType",
84
0
              &tmpi,
85
0
              0x0,
86
0
              error))
87
0
    return FALSE;
88
0
  self->dev_capability_type = tmpi;
89
90
  /* data */
91
0
  str = fwupd_json_object_get_string(json_obj, "ExtraData", NULL);
92
0
  if (str != NULL) {
93
0
    gsize bufsz = 0;
94
0
    g_autofree guchar *buf = g_base64_decode(str, &bufsz);
95
0
    g_autoptr(GInputStream) stream = NULL;
96
0
    g_autoptr(FuFirmware) img = fu_firmware_new();
97
98
    /* create child */
99
0
    stream = g_memory_input_stream_new_from_data(g_steal_pointer(&buf), bufsz, g_free);
100
0
    if (!fu_firmware_parse_stream(img,
101
0
                stream,
102
0
                0x0,
103
0
                FU_FIRMWARE_PARSE_FLAG_CACHE_BLOB,
104
0
                error))
105
0
      return FALSE;
106
0
    fu_firmware_set_id(img, FU_FIRMWARE_ID_PAYLOAD);
107
0
    if (!fu_firmware_add_image(FU_FIRMWARE(self), img, error))
108
0
      return FALSE;
109
0
  }
110
111
  /* success */
112
0
  return TRUE;
113
0
}
114
115
static void
116
fu_usb_bos_descriptor_add_json(FwupdCodec *codec, FwupdJsonObject *json_obj, FwupdCodecFlags flags)
117
0
{
118
0
  FuUsbBosDescriptor *self = FU_USB_BOS_DESCRIPTOR(codec);
119
0
  g_autoptr(GBytes) bytes = NULL;
120
121
  /* optional properties */
122
0
  if (self->dev_capability_type != 0) {
123
0
    fwupd_json_object_add_integer(json_obj,
124
0
                "DevCapabilityType",
125
0
                self->dev_capability_type);
126
0
  }
127
128
  /* data */
129
0
  bytes = fu_firmware_get_image_by_id_bytes(FU_FIRMWARE(self), FU_FIRMWARE_ID_PAYLOAD, NULL);
130
0
  if (bytes != NULL && g_bytes_get_size(bytes) > 0) {
131
0
    g_autofree gchar *str =
132
0
        g_base64_encode(g_bytes_get_data(bytes, NULL), g_bytes_get_size(bytes));
133
0
    fwupd_json_object_add_string(json_obj, "ExtraData", str);
134
0
  }
135
0
}
136
137
/**
138
 * fu_usb_bos_descriptor_get_capability:
139
 * @self: a #FuUsbBosDescriptor
140
 *
141
 * Gets the BOS descriptor capability.
142
 *
143
 * Return value: capability
144
 *
145
 * Since: 2.0.0
146
 **/
147
guint8
148
fu_usb_bos_descriptor_get_capability(FuUsbBosDescriptor *self)
149
0
{
150
0
  g_return_val_if_fail(FU_IS_USB_BOS_DESCRIPTOR(self), 0);
151
0
  return self->dev_capability_type;
152
0
}
153
154
static gboolean
155
fu_usb_bos_descriptor_parse(FuFirmware *firmware,
156
          GInputStream *stream,
157
          FuFirmwareParseFlags flags,
158
          GError **error)
159
0
{
160
0
  FuUsbBosDescriptor *self = FU_USB_BOS_DESCRIPTOR(firmware);
161
0
  g_autoptr(FuUsbBosHdr) st = NULL;
162
163
  /* FuUsbDescriptor */
164
0
  if (!FU_FIRMWARE_CLASS(fu_usb_bos_descriptor_parent_class)
165
0
     ->parse(firmware, stream, flags, error))
166
0
    return FALSE;
167
168
  /* parse */
169
0
  st = fu_usb_bos_hdr_parse_stream(stream, 0x0, error);
170
0
  if (st == NULL)
171
0
    return FALSE;
172
0
  self->length = fu_usb_bos_hdr_get_length(st);
173
0
  self->dev_capability_type = fu_usb_bos_hdr_get_dev_capability_type(st);
174
175
  /* data */
176
0
  if (self->length > st->buf->len) {
177
0
    g_autoptr(FuFirmware) img = fu_firmware_new();
178
0
    g_autoptr(GInputStream) img_stream = NULL;
179
180
0
    img_stream = fu_partial_input_stream_new(stream,
181
0
               st->buf->len,
182
0
               self->length - st->buf->len,
183
0
               error);
184
0
    if (img_stream == NULL) {
185
0
      g_prefix_error_literal(error, "failed to cut BOS descriptor: ");
186
0
      return FALSE;
187
0
    }
188
0
    if (!fu_firmware_parse_stream(img,
189
0
                img_stream,
190
0
                0x0,
191
0
                FU_FIRMWARE_PARSE_FLAG_CACHE_BLOB,
192
0
                error))
193
0
      return FALSE;
194
0
    fu_firmware_set_id(img, FU_FIRMWARE_ID_PAYLOAD);
195
0
    if (!fu_firmware_add_image(FU_FIRMWARE(self), img, error))
196
0
      return FALSE;
197
0
  }
198
199
  /* success */
200
0
  return TRUE;
201
0
}
202
203
static GByteArray *
204
fu_usb_bos_descriptor_write(FuFirmware *firmware, GError **error)
205
0
{
206
0
  FuUsbBosDescriptor *self = FU_USB_BOS_DESCRIPTOR(firmware);
207
0
  g_autoptr(FuUsbBosHdr) st = fu_usb_bos_hdr_new();
208
0
  g_autoptr(GBytes) blob = NULL;
209
210
0
  fu_usb_bos_hdr_set_dev_capability_type(st, self->dev_capability_type);
211
0
  blob = fu_firmware_get_image_by_id_bytes(firmware, FU_FIRMWARE_ID_PAYLOAD, NULL);
212
0
  if (blob != NULL)
213
0
    fu_byte_array_append_bytes(st->buf, blob);
214
0
  fu_usb_bos_hdr_set_length(st, st->buf->len);
215
216
  /* success */
217
0
  return g_steal_pointer(&st->buf);
218
0
}
219
220
static void
221
fu_usb_bos_descriptor_codec_iface_init(FwupdCodecInterface *iface)
222
0
{
223
0
  iface->add_json = fu_usb_bos_descriptor_add_json;
224
0
  iface->from_json = fu_usb_bos_descriptor_from_json;
225
0
}
226
227
static void
228
fu_usb_bos_descriptor_class_init(FuUsbBosDescriptorClass *klass)
229
0
{
230
0
  FuFirmwareClass *firmware_class = FU_FIRMWARE_CLASS(klass);
231
0
  firmware_class->parse = fu_usb_bos_descriptor_parse;
232
0
  firmware_class->write = fu_usb_bos_descriptor_write;
233
0
  firmware_class->build = fu_usb_bos_descriptor_build;
234
0
  firmware_class->export = fu_usb_bos_descriptor_export;
235
0
}
236
237
static void
238
fu_usb_bos_descriptor_init(FuUsbBosDescriptor *self)
239
0
{
240
0
  self->dev_capability_type = FU_USB_DESCRIPTOR_KIND_INTERFACE;
241
0
  fu_firmware_add_flag(FU_FIRMWARE(self), FU_FIRMWARE_FLAG_ALLOW_LINEAR);
242
0
  fu_firmware_add_flag(FU_FIRMWARE(self), FU_FIRMWARE_FLAG_NO_AUTO_DETECTION);
243
0
  fu_firmware_add_image_gtype(FU_FIRMWARE(self), FU_TYPE_FIRMWARE);
244
0
}
245
246
/**
247
 * fu_usb_bos_descriptor_new:
248
 * @st_hdr: a #FuUsbBosHdr
249
 *
250
 * Return value: a new #FuUsbBosDescriptor object.
251
 *
252
 * Since: 2.1.1
253
 **/
254
FuUsbBosDescriptor *
255
fu_usb_bos_descriptor_new(FuUsbBosHdr *st_hdr)
256
0
{
257
0
  g_autoptr(FuUsbBosDescriptor) self = g_object_new(FU_TYPE_USB_BOS_DESCRIPTOR, NULL);
258
0
  g_autoptr(FuFirmware) img = fu_firmware_new();
259
0
  g_autoptr(GBytes) bytes = NULL;
260
261
  /* copy the data */
262
0
  self->length = fu_usb_bos_hdr_get_length(st_hdr);
263
0
  self->dev_capability_type = fu_usb_bos_hdr_get_dev_capability_type(st_hdr);
264
0
  bytes = g_bytes_new(st_hdr->buf->data + FU_USB_BOS_HDR_SIZE,
265
0
          st_hdr->buf->len - FU_USB_BOS_HDR_SIZE);
266
0
  fu_firmware_set_bytes(img, bytes);
267
0
  fu_firmware_set_id(img, FU_FIRMWARE_ID_PAYLOAD);
268
0
  if (!fu_firmware_add_image(FU_FIRMWARE(self), img, NULL))
269
0
    return NULL;
270
0
  return g_steal_pointer(&self);
271
0
}