/src/fwupd/plugins/ccgx-dmc/fu-ccgx-dmc-firmware.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright 2020 Cypress Semiconductor Corporation. |
3 | | * Copyright 2020 Richard Hughes <richard@hughsie.com> |
4 | | * |
5 | | * SPDX-License-Identifier: LGPL-2.1-or-later |
6 | | */ |
7 | | |
8 | | #include "config.h" |
9 | | |
10 | | #include <string.h> |
11 | | |
12 | | #include "fu-ccgx-dmc-firmware.h" |
13 | | #include "fu-ccgx-dmc-struct.h" |
14 | | |
15 | | struct _FuCcgxDmcFirmware { |
16 | | FuFirmwareClass parent_instance; |
17 | | GPtrArray *image_records; |
18 | | GBytes *fwct_blob; |
19 | | GBytes *custom_meta_blob; |
20 | | guint32 row_data_offset_start; |
21 | | guint32 fw_data_size; |
22 | | }; |
23 | | |
24 | | G_DEFINE_TYPE(FuCcgxDmcFirmware, fu_ccgx_dmc_firmware, FU_TYPE_FIRMWARE) |
25 | | |
26 | 2.80k | #define DMC_FWCT_MAX_SIZE 2048 |
27 | 485 | #define DMC_HASH_SIZE 32 |
28 | 1.20k | #define DMC_CUSTOM_META_LENGTH_FIELD_SIZE 2 |
29 | | |
30 | | static void |
31 | | fu_ccgx_dmc_firmware_record_free(FuCcgxDmcFirmwareRecord *rcd) |
32 | 2.66k | { |
33 | 2.66k | if (rcd->seg_records != NULL) |
34 | 1.99k | g_ptr_array_unref(rcd->seg_records); |
35 | 2.66k | g_free(rcd); |
36 | 2.66k | } |
37 | | |
38 | | static void |
39 | | fu_ccgx_dmc_firmware_segment_record_free(FuCcgxDmcFirmwareSegmentRecord *rcd) |
40 | 24.2k | { |
41 | 24.2k | if (rcd->data_records != NULL) |
42 | 24.0k | g_ptr_array_unref(rcd->data_records); |
43 | 24.2k | g_free(rcd); |
44 | 24.2k | } |
45 | | |
46 | | G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuCcgxDmcFirmwareRecord, fu_ccgx_dmc_firmware_record_free) |
47 | | G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuCcgxDmcFirmwareSegmentRecord, |
48 | | fu_ccgx_dmc_firmware_segment_record_free) |
49 | | |
50 | | GPtrArray * |
51 | | fu_ccgx_dmc_firmware_get_image_records(FuCcgxDmcFirmware *self) |
52 | 0 | { |
53 | 0 | g_return_val_if_fail(FU_IS_CCGX_DMC_FIRMWARE(self), NULL); |
54 | 0 | return self->image_records; |
55 | 0 | } |
56 | | |
57 | | GBytes * |
58 | | fu_ccgx_dmc_firmware_get_fwct_record(FuCcgxDmcFirmware *self) |
59 | 0 | { |
60 | 0 | g_return_val_if_fail(FU_IS_CCGX_DMC_FIRMWARE(self), NULL); |
61 | 0 | return self->fwct_blob; |
62 | 0 | } |
63 | | |
64 | | GBytes * |
65 | | fu_ccgx_dmc_firmware_get_custom_meta_record(FuCcgxDmcFirmware *self) |
66 | 0 | { |
67 | 0 | g_return_val_if_fail(FU_IS_CCGX_DMC_FIRMWARE(self), NULL); |
68 | 0 | return self->custom_meta_blob; |
69 | 0 | } |
70 | | |
71 | | guint32 |
72 | | fu_ccgx_dmc_firmware_get_fw_data_size(FuCcgxDmcFirmware *self) |
73 | 0 | { |
74 | 0 | g_return_val_if_fail(FU_IS_CCGX_DMC_FIRMWARE(self), 0); |
75 | 0 | return self->fw_data_size; |
76 | 0 | } |
77 | | |
78 | | static void |
79 | | fu_ccgx_dmc_firmware_export(FuFirmware *firmware, FuFirmwareExportFlags flags, XbBuilderNode *bn) |
80 | 0 | { |
81 | 0 | FuCcgxDmcFirmware *self = FU_CCGX_DMC_FIRMWARE(firmware); |
82 | 0 | if (flags & FU_FIRMWARE_EXPORT_FLAG_INCLUDE_DEBUG) { |
83 | 0 | fu_xmlb_builder_insert_kx(bn, "fw_data_size", self->fw_data_size); |
84 | 0 | fu_xmlb_builder_insert_kx(bn, "image_records", self->image_records->len); |
85 | 0 | } |
86 | 0 | } |
87 | | |
88 | | static gboolean |
89 | | fu_ccgx_dmc_firmware_parse_segment(FuFirmware *firmware, |
90 | | GInputStream *stream, |
91 | | FuCcgxDmcFirmwareRecord *img_rcd, |
92 | | gsize *seg_off, |
93 | | FuFirmwareParseFlags flags, |
94 | | GError **error) |
95 | 1.99k | { |
96 | 1.99k | FuCcgxDmcFirmware *self = FU_CCGX_DMC_FIRMWARE(firmware); |
97 | 1.99k | gsize row_off; |
98 | 1.99k | g_autoptr(GChecksum) csum = g_checksum_new(G_CHECKSUM_SHA256); |
99 | | |
100 | | /* set row data offset in current image */ |
101 | 1.99k | row_off = self->row_data_offset_start + img_rcd->img_offset; |
102 | | |
103 | | /* parse segment in image */ |
104 | 1.99k | img_rcd->seg_records = |
105 | 1.99k | g_ptr_array_new_with_free_func((GFreeFunc)fu_ccgx_dmc_firmware_segment_record_free); |
106 | 25.7k | for (guint32 i = 0; i < img_rcd->num_img_segments; i++) { |
107 | 24.2k | guint16 row_size_bytes = 0; |
108 | 24.2k | g_autoptr(FuCcgxDmcFirmwareSegmentRecord) seg_rcd = NULL; |
109 | 24.2k | g_autoptr(GByteArray) st_info = NULL; |
110 | | |
111 | | /* read segment info */ |
112 | 24.2k | seg_rcd = g_new0(FuCcgxDmcFirmwareSegmentRecord, 1); |
113 | 24.2k | st_info = |
114 | 24.2k | fu_struct_ccgx_dmc_fwct_segmentation_info_parse_stream(stream, *seg_off, error); |
115 | 24.2k | if (st_info == NULL) |
116 | 193 | return FALSE; |
117 | 24.0k | seg_rcd->start_row = |
118 | 24.0k | fu_struct_ccgx_dmc_fwct_segmentation_info_get_start_row(st_info); |
119 | 24.0k | seg_rcd->num_rows = fu_struct_ccgx_dmc_fwct_segmentation_info_get_num_rows(st_info); |
120 | | |
121 | | /* calculate actual row size */ |
122 | 24.0k | row_size_bytes = img_rcd->row_size * 64; |
123 | | |
124 | | /* create data record array in segment record */ |
125 | 24.0k | seg_rcd->data_records = |
126 | 24.0k | g_ptr_array_new_with_free_func((GDestroyNotify)g_bytes_unref); |
127 | | |
128 | | /* read row data in segment */ |
129 | 592k | for (int row = 0; row < seg_rcd->num_rows; row++) { |
130 | 569k | g_autoptr(GBytes) data_rcd = NULL; |
131 | | |
132 | 569k | data_rcd = fu_input_stream_read_bytes(stream, |
133 | 569k | row_off, |
134 | 569k | row_size_bytes, |
135 | 569k | NULL, |
136 | 569k | error); |
137 | 569k | if (data_rcd == NULL) |
138 | 308 | return FALSE; |
139 | | |
140 | | /* update hash */ |
141 | 568k | g_checksum_update(csum, |
142 | 568k | (guchar *)g_bytes_get_data(data_rcd, NULL), |
143 | 568k | g_bytes_get_size(data_rcd)); |
144 | | |
145 | | /* add row data to data record */ |
146 | 568k | g_ptr_array_add(seg_rcd->data_records, g_steal_pointer(&data_rcd)); |
147 | | |
148 | | /* increment row data offset */ |
149 | 568k | row_off += row_size_bytes; |
150 | 568k | } |
151 | | |
152 | | /* add segment record to segment array */ |
153 | 23.7k | g_ptr_array_add(img_rcd->seg_records, g_steal_pointer(&seg_rcd)); |
154 | | |
155 | | /* increment segment info offset */ |
156 | 23.7k | *seg_off += st_info->len; |
157 | 23.7k | } |
158 | | |
159 | | /* check checksum */ |
160 | 1.48k | if ((flags & FU_FIRMWARE_PARSE_FLAG_IGNORE_CHECKSUM) == 0) { |
161 | 485 | guint8 csumbuf[DMC_HASH_SIZE] = {0x0}; |
162 | 485 | gsize csumbufsz = sizeof(csumbuf); |
163 | 485 | g_checksum_get_digest(csum, csumbuf, &csumbufsz); |
164 | 485 | if (memcmp(csumbuf, img_rcd->img_digest, DMC_HASH_SIZE) != 0) { |
165 | 257 | g_set_error_literal(error, |
166 | 257 | FWUPD_ERROR, |
167 | 257 | FWUPD_ERROR_NOT_SUPPORTED, |
168 | 257 | "invalid hash"); |
169 | 257 | return FALSE; |
170 | 257 | } |
171 | 485 | } |
172 | | |
173 | | /* success */ |
174 | 1.23k | return TRUE; |
175 | 1.48k | } |
176 | | |
177 | | static gboolean |
178 | | fu_ccgx_dmc_firmware_parse_image(FuFirmware *firmware, |
179 | | guint8 image_count, |
180 | | GInputStream *stream, |
181 | | FuFirmwareParseFlags flags, |
182 | | GError **error) |
183 | 1.20k | { |
184 | 1.20k | FuCcgxDmcFirmware *self = FU_CCGX_DMC_FIRMWARE(firmware); |
185 | 1.20k | gsize img_off = FU_STRUCT_CCGX_DMC_FWCT_INFO_SIZE; |
186 | 1.20k | gsize seg_off = FU_STRUCT_CCGX_DMC_FWCT_INFO_SIZE + |
187 | 1.20k | image_count * FU_STRUCT_CCGX_DMC_FWCT_IMAGE_INFO_SIZE; |
188 | | |
189 | | /* set initial segment info offset */ |
190 | 2.92k | for (guint32 i = 0; i < image_count; i++) { |
191 | 2.66k | g_autoptr(FuCcgxDmcFirmwareRecord) img_rcd = NULL; |
192 | 2.66k | g_autoptr(GByteArray) st_img = NULL; |
193 | | |
194 | | /* read image info */ |
195 | 2.66k | img_rcd = g_new0(FuCcgxDmcFirmwareRecord, 1); |
196 | 2.66k | st_img = fu_struct_ccgx_dmc_fwct_image_info_parse_stream(stream, img_off, error); |
197 | 2.66k | if (st_img == NULL) |
198 | 171 | return FALSE; |
199 | 2.49k | img_rcd->row_size = fu_struct_ccgx_dmc_fwct_image_info_get_row_size(st_img); |
200 | 2.49k | if (img_rcd->row_size == 0) { |
201 | 20 | g_set_error(error, |
202 | 20 | FWUPD_ERROR, |
203 | 20 | FWUPD_ERROR_NOT_SUPPORTED, |
204 | 20 | "invalid row size 0x%x", |
205 | 20 | img_rcd->row_size); |
206 | 20 | return FALSE; |
207 | 20 | } |
208 | 2.47k | img_rcd->img_offset = fu_struct_ccgx_dmc_fwct_image_info_get_img_offset(st_img); |
209 | 2.47k | img_rcd->num_img_segments = |
210 | 2.47k | fu_struct_ccgx_dmc_fwct_image_info_get_num_img_segments(st_img); |
211 | | |
212 | | /* segments are optional */ |
213 | 2.47k | if (img_rcd->num_img_segments > 0) { |
214 | 1.99k | gsize img_digestsz = 0; |
215 | 1.99k | const guint8 *img_digest; |
216 | | |
217 | 1.99k | img_digest = |
218 | 1.99k | fu_struct_ccgx_dmc_fwct_image_info_get_img_digest(st_img, |
219 | 1.99k | &img_digestsz); |
220 | 1.99k | if (!fu_memcpy_safe((guint8 *)&img_rcd->img_digest, |
221 | 1.99k | sizeof(img_rcd->img_digest), |
222 | 1.99k | 0x0, /* dst */ |
223 | 1.99k | img_digest, |
224 | 1.99k | img_digestsz, |
225 | 1.99k | 0, /* src */ |
226 | 1.99k | img_digestsz, |
227 | 1.99k | error)) |
228 | 0 | return FALSE; |
229 | | |
230 | | /* parse segment */ |
231 | 1.99k | if (!fu_ccgx_dmc_firmware_parse_segment(firmware, |
232 | 1.99k | stream, |
233 | 1.99k | img_rcd, |
234 | 1.99k | &seg_off, |
235 | 1.99k | flags, |
236 | 1.99k | error)) |
237 | 758 | return FALSE; |
238 | | |
239 | | /* add image record to image record array */ |
240 | 1.23k | g_ptr_array_add(self->image_records, g_steal_pointer(&img_rcd)); |
241 | 1.23k | } |
242 | | |
243 | | /* increment image offset */ |
244 | 1.72k | img_off += FU_STRUCT_CCGX_DMC_FWCT_IMAGE_INFO_SIZE; |
245 | 1.72k | } |
246 | | |
247 | 256 | return TRUE; |
248 | 1.20k | } |
249 | | |
250 | | static gboolean |
251 | | fu_ccgx_dmc_firmware_validate(FuFirmware *firmware, |
252 | | GInputStream *stream, |
253 | | gsize offset, |
254 | | GError **error) |
255 | 7.11M | { |
256 | 7.11M | return fu_struct_ccgx_dmc_fwct_info_validate_stream(stream, offset, error); |
257 | 7.11M | } |
258 | | |
259 | | static gboolean |
260 | | fu_ccgx_dmc_firmware_parse(FuFirmware *firmware, |
261 | | GInputStream *stream, |
262 | | FuFirmwareParseFlags flags, |
263 | | GError **error) |
264 | 1.39k | { |
265 | 1.39k | FuCcgxDmcFirmware *self = FU_CCGX_DMC_FIRMWARE(firmware); |
266 | 1.39k | gsize streamsz = 0; |
267 | 1.39k | guint16 hdr_size = 0; |
268 | 1.39k | guint16 mdbufsz = 0; |
269 | 1.39k | guint32 hdr_composite_version = 0; |
270 | 1.39k | guint8 hdr_image_count = 0; |
271 | 1.39k | g_autoptr(GByteArray) st_hdr = NULL; |
272 | | |
273 | | /* parse */ |
274 | 1.39k | st_hdr = fu_struct_ccgx_dmc_fwct_info_parse_stream(stream, 0x0, error); |
275 | 1.39k | if (st_hdr == NULL) |
276 | 0 | return FALSE; |
277 | | |
278 | | /* check fwct size */ |
279 | 1.39k | hdr_size = fu_struct_ccgx_dmc_fwct_info_get_size(st_hdr); |
280 | 1.39k | if (hdr_size > DMC_FWCT_MAX_SIZE || hdr_size == 0) { |
281 | 21 | g_set_error(error, |
282 | 21 | FWUPD_ERROR, |
283 | 21 | FWUPD_ERROR_NOT_SUPPORTED, |
284 | 21 | "invalid dmc fwct size, expected <= 0x%x, got 0x%x", |
285 | 21 | (guint)DMC_FWCT_MAX_SIZE, |
286 | 21 | (guint)hdr_size); |
287 | 21 | return FALSE; |
288 | 21 | } |
289 | | |
290 | | /* set version */ |
291 | 1.37k | hdr_composite_version = fu_struct_ccgx_dmc_fwct_info_get_composite_version(st_hdr); |
292 | 1.37k | if (hdr_composite_version != 0) |
293 | 1.26k | fu_firmware_set_version_raw(firmware, hdr_composite_version); |
294 | | |
295 | | /* read fwct data */ |
296 | 1.37k | self->fwct_blob = fu_input_stream_read_bytes(stream, 0x0, hdr_size, NULL, error); |
297 | 1.37k | if (self->fwct_blob == NULL) |
298 | 0 | return FALSE; |
299 | | |
300 | | /* create custom meta binary */ |
301 | 1.37k | if (!fu_input_stream_read_u16(stream, hdr_size, &mdbufsz, G_LITTLE_ENDIAN, error)) { |
302 | 160 | g_prefix_error(error, "failed to read metadata size: "); |
303 | 160 | return FALSE; |
304 | 160 | } |
305 | 1.21k | if (mdbufsz > 0) { |
306 | 707 | self->custom_meta_blob = |
307 | 707 | fu_input_stream_read_bytes(stream, hdr_size + 2, mdbufsz, NULL, error); |
308 | 707 | if (self->custom_meta_blob == NULL) |
309 | 6 | return FALSE; |
310 | 707 | } |
311 | | |
312 | | /* set row data start offset */ |
313 | 1.20k | self->row_data_offset_start = hdr_size + DMC_CUSTOM_META_LENGTH_FIELD_SIZE + mdbufsz; |
314 | 1.20k | if (!fu_input_stream_size(stream, &streamsz, error)) |
315 | 0 | return FALSE; |
316 | 1.20k | self->fw_data_size = streamsz - self->row_data_offset_start; |
317 | | |
318 | | /* parse image */ |
319 | 1.20k | hdr_image_count = fu_struct_ccgx_dmc_fwct_info_get_image_count(st_hdr); |
320 | 1.20k | if (!fu_ccgx_dmc_firmware_parse_image(firmware, hdr_image_count, stream, flags, error)) |
321 | 949 | return FALSE; |
322 | | |
323 | | /* success */ |
324 | 256 | return TRUE; |
325 | 1.20k | } |
326 | | |
327 | | static GByteArray * |
328 | | fu_ccgx_dmc_firmware_write(FuFirmware *firmware, GError **error) |
329 | 256 | { |
330 | 256 | g_autoptr(GByteArray) buf = g_byte_array_new(); |
331 | 256 | g_autoptr(GByteArray) st_hdr = fu_struct_ccgx_dmc_fwct_info_new(); |
332 | 256 | g_autoptr(GPtrArray) images = fu_firmware_get_images(firmware); |
333 | | |
334 | | /* add header */ |
335 | 256 | fu_struct_ccgx_dmc_fwct_info_set_size( |
336 | 256 | st_hdr, |
337 | 256 | FU_STRUCT_CCGX_DMC_FWCT_INFO_SIZE + |
338 | 256 | (images->len * (FU_STRUCT_CCGX_DMC_FWCT_IMAGE_INFO_SIZE + |
339 | 256 | FU_STRUCT_CCGX_DMC_FWCT_SEGMENTATION_INFO_SIZE))); |
340 | 256 | fu_struct_ccgx_dmc_fwct_info_set_version(st_hdr, 0x2); |
341 | 256 | fu_struct_ccgx_dmc_fwct_info_set_custom_meta_type(st_hdr, 0x3); |
342 | 256 | fu_struct_ccgx_dmc_fwct_info_set_cdtt_version(st_hdr, 0x1); |
343 | 256 | fu_struct_ccgx_dmc_fwct_info_set_device_id(st_hdr, 0x1); |
344 | 256 | fu_struct_ccgx_dmc_fwct_info_set_composite_version(st_hdr, |
345 | 256 | fu_firmware_get_version_raw(firmware)); |
346 | 256 | fu_struct_ccgx_dmc_fwct_info_set_image_count(st_hdr, images->len); |
347 | 256 | g_byte_array_append(buf, st_hdr->data, st_hdr->len); |
348 | | |
349 | | /* add image headers */ |
350 | 256 | for (guint i = 0; i < images->len; i++) { |
351 | 0 | g_autoptr(GByteArray) st_img = fu_struct_ccgx_dmc_fwct_image_info_new(); |
352 | 0 | fu_struct_ccgx_dmc_fwct_image_info_set_device_type(st_img, 0x2); |
353 | 0 | fu_struct_ccgx_dmc_fwct_image_info_set_img_type(st_img, 0x1); |
354 | 0 | fu_struct_ccgx_dmc_fwct_image_info_set_row_size(st_img, 0x1); |
355 | 0 | fu_struct_ccgx_dmc_fwct_image_info_set_fw_version(st_img, 0x330006d2); |
356 | 0 | fu_struct_ccgx_dmc_fwct_image_info_set_app_version(st_img, 0x14136161); |
357 | 0 | fu_struct_ccgx_dmc_fwct_image_info_set_num_img_segments(st_img, 0x1); |
358 | 0 | g_byte_array_append(buf, st_img->data, st_img->len); |
359 | 0 | } |
360 | | |
361 | | /* add segments */ |
362 | 256 | for (guint i = 0; i < images->len; i++) { |
363 | 0 | FuFirmware *img = g_ptr_array_index(images, i); |
364 | 0 | g_autoptr(GByteArray) st_info = fu_struct_ccgx_dmc_fwct_segmentation_info_new(); |
365 | 0 | g_autoptr(FuChunkArray) chunks = NULL; |
366 | 0 | g_autoptr(GBytes) img_bytes = fu_firmware_get_bytes(img, error); |
367 | 0 | if (img_bytes == NULL) |
368 | 0 | return NULL; |
369 | 0 | chunks = fu_chunk_array_new_from_bytes(img_bytes, |
370 | 0 | FU_CHUNK_ADDR_OFFSET_NONE, |
371 | 0 | FU_CHUNK_PAGESZ_NONE, |
372 | 0 | 64); |
373 | 0 | fu_struct_ccgx_dmc_fwct_segmentation_info_set_num_rows( |
374 | 0 | st_info, |
375 | 0 | MAX(fu_chunk_array_length(chunks), 1)); |
376 | 0 | g_byte_array_append(buf, st_info->data, st_info->len); |
377 | 0 | } |
378 | | |
379 | | /* metadata */ |
380 | 256 | fu_byte_array_append_uint16(buf, 0x1, G_LITTLE_ENDIAN); |
381 | 256 | fu_byte_array_append_uint8(buf, 0xff); |
382 | | |
383 | | /* add image headers */ |
384 | 256 | for (guint i = 0; i < images->len; i++) { |
385 | 0 | FuFirmware *img = g_ptr_array_index(images, i); |
386 | 0 | gsize csumbufsz = DMC_HASH_SIZE; |
387 | 0 | gsize img_offset = FU_STRUCT_CCGX_DMC_FWCT_INFO_SIZE + |
388 | 0 | (i * FU_STRUCT_CCGX_DMC_FWCT_IMAGE_INFO_SIZE); |
389 | 0 | guint8 csumbuf[DMC_HASH_SIZE] = {0x0}; |
390 | 0 | g_autoptr(GChecksum) csum = g_checksum_new(G_CHECKSUM_SHA256); |
391 | 0 | g_autoptr(GBytes) img_bytes = NULL; |
392 | 0 | g_autoptr(GBytes) img_padded = NULL; |
393 | 0 | g_autoptr(FuChunkArray) chunks = NULL; |
394 | |
|
395 | 0 | img_bytes = fu_firmware_get_bytes(img, error); |
396 | 0 | if (img_bytes == NULL) |
397 | 0 | return NULL; |
398 | 0 | chunks = fu_chunk_array_new_from_bytes(img_bytes, |
399 | 0 | FU_CHUNK_ADDR_OFFSET_NONE, |
400 | 0 | FU_CHUNK_PAGESZ_NONE, |
401 | 0 | 64); |
402 | 0 | img_padded = |
403 | 0 | fu_bytes_pad(img_bytes, MAX(fu_chunk_array_length(chunks), 1) * 64, 0xFF); |
404 | 0 | fu_byte_array_append_bytes(buf, img_padded); |
405 | 0 | g_checksum_update(csum, |
406 | 0 | (const guchar *)g_bytes_get_data(img_padded, NULL), |
407 | 0 | g_bytes_get_size(img_padded)); |
408 | 0 | g_checksum_get_digest(csum, csumbuf, &csumbufsz); |
409 | | |
410 | | /* update checksum */ |
411 | 0 | if (!fu_memcpy_safe(buf->data, |
412 | 0 | buf->len, /* dst */ |
413 | 0 | img_offset + |
414 | 0 | FU_STRUCT_CCGX_DMC_FWCT_IMAGE_INFO_OFFSET_IMG_DIGEST, |
415 | 0 | csumbuf, |
416 | 0 | sizeof(csumbuf), |
417 | 0 | 0x0, /* src */ |
418 | 0 | sizeof(csumbuf), |
419 | 0 | error)) |
420 | 0 | return NULL; |
421 | 0 | } |
422 | | |
423 | 256 | return g_steal_pointer(&buf); |
424 | 256 | } |
425 | | |
426 | | static gchar * |
427 | | fu_ccgx_dmc_firmware_convert_version(FuFirmware *firmware, guint64 version_raw) |
428 | 1.26k | { |
429 | 1.26k | return fu_version_from_uint32(version_raw, fu_firmware_get_version_format(firmware)); |
430 | 1.26k | } |
431 | | |
432 | | static void |
433 | | fu_ccgx_dmc_firmware_init(FuCcgxDmcFirmware *self) |
434 | 1.97k | { |
435 | 1.97k | self->image_records = |
436 | 1.97k | g_ptr_array_new_with_free_func((GFreeFunc)fu_ccgx_dmc_firmware_record_free); |
437 | 1.97k | fu_firmware_add_flag(FU_FIRMWARE(self), FU_FIRMWARE_FLAG_HAS_CHECKSUM); |
438 | 1.97k | fu_firmware_set_version_format(FU_FIRMWARE(self), FWUPD_VERSION_FORMAT_QUAD); |
439 | 1.97k | } |
440 | | |
441 | | static void |
442 | | fu_ccgx_dmc_firmware_finalize(GObject *object) |
443 | 1.97k | { |
444 | 1.97k | FuCcgxDmcFirmware *self = FU_CCGX_DMC_FIRMWARE(object); |
445 | | |
446 | 1.97k | if (self->fwct_blob != NULL) |
447 | 1.37k | g_bytes_unref(self->fwct_blob); |
448 | 1.97k | if (self->custom_meta_blob != NULL) |
449 | 701 | g_bytes_unref(self->custom_meta_blob); |
450 | 1.97k | if (self->image_records != NULL) |
451 | 1.97k | g_ptr_array_unref(self->image_records); |
452 | | |
453 | 1.97k | G_OBJECT_CLASS(fu_ccgx_dmc_firmware_parent_class)->finalize(object); |
454 | 1.97k | } |
455 | | |
456 | | static void |
457 | | fu_ccgx_dmc_firmware_class_init(FuCcgxDmcFirmwareClass *klass) |
458 | 1 | { |
459 | 1 | GObjectClass *object_class = G_OBJECT_CLASS(klass); |
460 | 1 | FuFirmwareClass *firmware_class = FU_FIRMWARE_CLASS(klass); |
461 | 1 | firmware_class->convert_version = fu_ccgx_dmc_firmware_convert_version; |
462 | 1 | object_class->finalize = fu_ccgx_dmc_firmware_finalize; |
463 | 1 | firmware_class->validate = fu_ccgx_dmc_firmware_validate; |
464 | 1 | firmware_class->parse = fu_ccgx_dmc_firmware_parse; |
465 | 1 | firmware_class->write = fu_ccgx_dmc_firmware_write; |
466 | 1 | firmware_class->export = fu_ccgx_dmc_firmware_export; |
467 | 1 | } |
468 | | |
469 | | FuFirmware * |
470 | | fu_ccgx_dmc_firmware_new(void) |
471 | 0 | { |
472 | 0 | return FU_FIRMWARE(g_object_new(FU_TYPE_CCGX_DMC_FIRMWARE, NULL)); |
473 | 0 | } |