/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 | } |