Coverage Report

Created: 2026-04-28 06:49

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/fwupd/plugins/wacom-usb/fu-wacom-usb-firmware.c
Line
Count
Source
1
/*
2
 * Copyright 2018 Richard Hughes <richard@hughsie.com>
3
 *
4
 * SPDX-License-Identifier: LGPL-2.1-or-later
5
 */
6
7
#include "config.h"
8
9
#include <string.h>
10
11
#include "fu-wacom-usb-firmware.h"
12
#include "fu-wacom-usb-struct.h"
13
14
struct _FuWacomUsbFirmware {
15
  FuFirmware parent_instance;
16
};
17
18
1.87k
G_DEFINE_TYPE(FuWacomUsbFirmware, fu_wacom_usb_firmware, FU_TYPE_FIRMWARE)
19
1.87k
20
1.85M
#define FU_WACOM_USB_FIRMWARE_TOKENS_MAX   100000 /* lines */
21
1.80k
#define FU_WACOM_USB_FIRMWARE_SECTIONS_MAX 10
22
23
typedef struct {
24
  guint32 addr;
25
  guint32 sz;
26
  guint32 prog_start_addr;
27
} FuFirmwareWacHeaderRecord;
28
29
typedef struct {
30
  FuFirmware *firmware;
31
  FuFirmwareParseFlags flags;
32
  GPtrArray *header_infos;
33
  GString *image_buffer;
34
  guint8 images_cnt;
35
} FuWacomUsbFirmwareTokenHelper;
36
37
static gboolean
38
fu_wacom_usb_firmware_tokenize_cb(GString *token,
39
          guint token_idx,
40
          gpointer user_data,
41
          GError **error)
42
1.85M
{
43
1.85M
  FuWacomUsbFirmwareTokenHelper *helper = (FuWacomUsbFirmwareTokenHelper *)user_data;
44
1.85M
  g_autofree gchar *cmd = NULL;
45
46
  /* sanity check */
47
1.85M
  if (token_idx > FU_WACOM_USB_FIRMWARE_TOKENS_MAX) {
48
1
    g_set_error_literal(error,
49
1
            FWUPD_ERROR,
50
1
            FWUPD_ERROR_INVALID_DATA,
51
1
            "file has too many lines");
52
1
    return FALSE;
53
1
  }
54
55
  /* remove WIN32 line endings */
56
1.85M
  g_strdelimit(token->str, "\r\x1a", '\0');
57
1.85M
  token->len = strlen(token->str);
58
59
  /* ignore blank lines */
60
1.85M
  cmd = g_strndup(token->str, 2);
61
1.85M
  if (g_strcmp0(cmd, "") == 0)
62
849k
    return TRUE;
63
64
  /* custom metadata */
65
1.00M
  if (g_strcmp0(cmd, "WA") == 0) {
66
    /* header info record */
67
5.59k
    if (token->len > 3 && memcmp(token->str + 2, "COM", 3) == 0) {
68
1.88k
      guint8 header_image_cnt = 0;
69
1.88k
      if (token->len != 40) {
70
74
        g_set_error(error,
71
74
              FWUPD_ERROR,
72
74
              FWUPD_ERROR_INTERNAL,
73
74
              "invalid header, got %" G_GSIZE_FORMAT " bytes",
74
74
              token->len);
75
74
        return FALSE;
76
74
      }
77
78
      /* sanity check */
79
1.80k
      if (helper->header_infos->len > FU_WACOM_USB_FIRMWARE_SECTIONS_MAX) {
80
2
        g_set_error(error,
81
2
              FWUPD_ERROR,
82
2
              FWUPD_ERROR_INTERNAL,
83
2
              "too many metadata sections: %u",
84
2
              helper->header_infos->len);
85
2
        return FALSE;
86
2
      }
87
1.80k
      if (!fu_firmware_strparse_uint4_safe(token->str,
88
1.80k
                   token->len,
89
1.80k
                   5,
90
1.80k
                   &header_image_cnt,
91
1.80k
                   error))
92
1
        return FALSE;
93
4.29k
      for (guint j = 0; j < header_image_cnt; j++) {
94
2.55k
        g_autofree FuFirmwareWacHeaderRecord *hdr = NULL;
95
2.55k
        hdr = g_new0(FuFirmwareWacHeaderRecord, 1);
96
2.55k
        if (!fu_firmware_strparse_uint32_safe(token->str,
97
2.55k
                      token->len,
98
2.55k
                      (j * 16) + 6,
99
2.55k
                      &hdr->addr,
100
2.55k
                      error))
101
35
          return FALSE;
102
2.51k
        if (!fu_firmware_strparse_uint32_safe(token->str,
103
2.51k
                      token->len,
104
2.51k
                      (j * 16) + 14,
105
2.51k
                      &hdr->sz,
106
2.51k
                      error))
107
22
          return FALSE;
108
2.49k
        g_debug("header_fw%u_addr: 0x%x", j, hdr->addr);
109
2.49k
        g_debug("header_fw%u_sz:   0x%x", j, hdr->sz);
110
2.49k
        g_ptr_array_add(helper->header_infos, g_steal_pointer(&hdr));
111
2.49k
      }
112
1.74k
      return TRUE;
113
1.80k
    }
114
115
    /* firmware headline record */
116
3.71k
    if (token->len == 13) {
117
569
      FuFirmwareWacHeaderRecord *hdr;
118
569
      guint8 idx = 0;
119
569
      if (!fu_firmware_strparse_uint4_safe(token->str,
120
569
                   token->len,
121
569
                   2,
122
569
                   &idx,
123
569
                   error))
124
3
        return FALSE;
125
566
      if (idx == 0) {
126
1
        g_set_error(error,
127
1
              FWUPD_ERROR,
128
1
              FWUPD_ERROR_INTERNAL,
129
1
              "headline %u invalid",
130
1
              idx);
131
1
        return FALSE;
132
1
      }
133
565
      if (idx > helper->header_infos->len) {
134
4
        g_set_error(error,
135
4
              FWUPD_ERROR,
136
4
              FWUPD_ERROR_INTERNAL,
137
4
              "headline %u exceeds header count %u",
138
4
              idx,
139
4
              helper->header_infos->len);
140
4
        return FALSE;
141
4
      }
142
561
      if (idx - 1 != helper->images_cnt) {
143
6
        g_set_error(error,
144
6
              FWUPD_ERROR,
145
6
              FWUPD_ERROR_INTERNAL,
146
6
              "headline %u is not in sorted order",
147
6
              idx);
148
6
        return FALSE;
149
6
      }
150
555
      hdr = g_ptr_array_index(helper->header_infos, idx - 1);
151
555
      if (!fu_firmware_strparse_uint32_safe(token->str,
152
555
                    token->len,
153
555
                    3,
154
555
                    &hdr->prog_start_addr,
155
555
                    error))
156
1
        return FALSE;
157
554
      if (hdr->prog_start_addr != hdr->addr) {
158
47
        g_set_error(error,
159
47
              FWUPD_ERROR,
160
47
              FWUPD_ERROR_INTERNAL,
161
47
              "programming address 0x%x != "
162
47
              "base address 0x%0x for idx %u",
163
47
              hdr->prog_start_addr,
164
47
              hdr->addr,
165
47
              idx);
166
47
        return FALSE;
167
47
      }
168
507
      g_debug("programing-start-address: 0x%x", hdr->prog_start_addr);
169
507
      return TRUE;
170
554
    }
171
172
3.14k
    g_debug("unknown Wacom-specific metadata");
173
3.14k
    return TRUE;
174
3.71k
  }
175
176
  /* start */
177
1.00M
  if (g_strcmp0(cmd, "S0") == 0) {
178
2.26k
    if (helper->image_buffer->len > 0) {
179
10
      g_set_error_literal(error,
180
10
              FWUPD_ERROR,
181
10
              FWUPD_ERROR_INTERNAL,
182
10
              "duplicate S0 without S7");
183
10
      return FALSE;
184
10
    }
185
2.25k
    g_string_append_printf(helper->image_buffer, "%s\n", token->str);
186
2.25k
    return TRUE;
187
2.26k
  }
188
189
  /* these are things we want to include in the image */
190
1.00M
  if (g_strcmp0(cmd, "S1") == 0 || g_strcmp0(cmd, "S2") == 0 || g_strcmp0(cmd, "S3") == 0 ||
191
11.1k
      g_strcmp0(cmd, "S5") == 0 || g_strcmp0(cmd, "S7") == 0 || g_strcmp0(cmd, "S8") == 0 ||
192
1.00M
      g_strcmp0(cmd, "S9") == 0) {
193
1.00M
    if (helper->image_buffer->len == 0) {
194
18
      g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "%s without S0", cmd);
195
18
      return FALSE;
196
18
    }
197
1.00M
    g_string_append_printf(helper->image_buffer, "%s\n", token->str);
198
1.00M
  } else {
199
62
    g_set_error(error,
200
62
          FWUPD_ERROR,
201
62
          FWUPD_ERROR_INTERNAL,
202
62
          "invalid SREC command on line %u: %s",
203
62
          token_idx + 1,
204
62
          cmd);
205
62
    return FALSE;
206
62
  }
207
208
  /* end */
209
1.00M
  if (g_strcmp0(cmd, "S7") == 0) {
210
1.99k
    g_autoptr(GBytes) blob = NULL;
211
1.99k
    g_autoptr(GBytes) fw_srec = NULL;
212
1.99k
    g_autoptr(FuFirmware) firmware_srec = fu_srec_firmware_new();
213
1.99k
    g_autoptr(FuFirmware) img = fu_firmware_new();
214
1.99k
    FuFirmwareWacHeaderRecord *hdr;
215
216
    /* get the correct relocated start address */
217
1.99k
    if (helper->images_cnt >= helper->header_infos->len) {
218
27
      g_set_error(error,
219
27
            FWUPD_ERROR,
220
27
            FWUPD_ERROR_INTERNAL,
221
27
            "%s without header",
222
27
            cmd);
223
27
      return FALSE;
224
27
    }
225
1.97k
    hdr = g_ptr_array_index(helper->header_infos, helper->images_cnt);
226
227
1.97k
    if (helper->image_buffer->len == 0) {
228
0
      g_set_error(error,
229
0
            FWUPD_ERROR,
230
0
            FWUPD_ERROR_INTERNAL,
231
0
            "%s with missing image buffer",
232
0
            cmd);
233
0
      return FALSE;
234
0
    }
235
236
    /* parse SREC file and add as image */
237
1.97k
    blob = g_bytes_new(helper->image_buffer->str, helper->image_buffer->len);
238
1.97k
    fu_srec_firmware_set_addr_min(FU_SREC_FIRMWARE(firmware_srec), hdr->addr);
239
1.97k
    if (!fu_firmware_parse_bytes(firmware_srec,
240
1.97k
               blob,
241
1.97k
               0x0,
242
1.97k
               helper->flags | FU_FIRMWARE_PARSE_FLAG_NO_SEARCH,
243
1.97k
               error))
244
372
      return FALSE;
245
1.59k
    fw_srec = fu_firmware_get_bytes(firmware_srec, error);
246
1.59k
    if (fw_srec == NULL)
247
0
      return FALSE;
248
1.59k
    fu_firmware_set_bytes(img, fw_srec);
249
1.59k
    fu_firmware_set_addr(img, fu_firmware_get_addr(firmware_srec));
250
1.59k
    fu_firmware_set_idx(img, helper->images_cnt);
251
1.59k
    if (!fu_firmware_add_image(helper->firmware, img, error))
252
0
      return FALSE;
253
1.59k
    helper->images_cnt++;
254
255
    /* clear the image buffer */
256
1.59k
    g_string_set_size(helper->image_buffer, 0);
257
1.59k
  }
258
259
  /* success */
260
1.00M
  return TRUE;
261
1.00M
}
262
263
static gboolean
264
fu_wacom_usb_firmware_validate(FuFirmware *firmware,
265
             GInputStream *stream,
266
             gsize offset,
267
             GError **error)
268
1.87k
{
269
1.87k
  return fu_struct_wacom_usb_firmware_hdr_validate_stream(stream, offset, error);
270
1.87k
}
271
272
static gboolean
273
fu_wacom_usb_firmware_parse(FuFirmware *firmware,
274
          GInputStream *stream,
275
          FuFirmwareParseFlags flags,
276
          GError **error)
277
1.82k
{
278
1.82k
  g_autoptr(GPtrArray) header_infos = g_ptr_array_new_with_free_func(g_free);
279
1.82k
  g_autoptr(GString) image_buffer = g_string_new(NULL);
280
1.82k
  FuWacomUsbFirmwareTokenHelper helper = {.firmware = firmware,
281
1.82k
            .flags = flags,
282
1.82k
            .header_infos = header_infos,
283
1.82k
            .image_buffer = image_buffer,
284
1.82k
            .images_cnt = 0};
285
286
  /* tokenize */
287
1.82k
  if (!fu_strsplit_stream(stream,
288
1.82k
        0x0,
289
1.82k
        "\n",
290
1.82k
        fu_wacom_usb_firmware_tokenize_cb,
291
1.82k
        &helper,
292
1.82k
        error))
293
686
    return FALSE;
294
295
  /* verify data is complete */
296
1.14k
  if (helper.image_buffer->len > 0) {
297
234
    g_set_error_literal(error,
298
234
            FWUPD_ERROR,
299
234
            FWUPD_ERROR_INTERNAL,
300
234
            "truncated data: no S7");
301
234
    return FALSE;
302
234
  }
303
304
  /* ensure this matched the header */
305
906
  if (helper.header_infos->len != helper.images_cnt) {
306
66
    g_set_error(error,
307
66
          FWUPD_ERROR,
308
66
          FWUPD_ERROR_INTERNAL,
309
66
          "not enough images %u for header count %u",
310
66
          helper.images_cnt,
311
66
          header_infos->len);
312
66
    return FALSE;
313
66
  }
314
315
  /* success */
316
840
  return TRUE;
317
906
}
318
319
static guint8
320
fu_wacom_usb_firmware_calc_checksum(GByteArray *buf)
321
1.11k
{
322
1.11k
  return fu_sum8(buf->data, buf->len) ^ 0xFF;
323
1.11k
}
324
325
static GByteArray *
326
fu_wacom_usb_firmware_write(FuFirmware *firmware, GError **error)
327
840
{
328
840
  g_autoptr(GPtrArray) images = fu_firmware_get_images(firmware);
329
840
  g_autoptr(GString) str = g_string_new(NULL);
330
840
  g_autoptr(GByteArray) buf = g_byte_array_new();
331
840
  g_autoptr(GByteArray) buf_hdr = g_byte_array_new();
332
333
  /* fw header */
334
840
  if (images->len == 0) {
335
504
    g_set_error_literal(error,
336
504
            FWUPD_ERROR,
337
504
            FWUPD_ERROR_NOT_SUPPORTED,
338
504
            "no firmware images found");
339
504
    return NULL;
340
504
  }
341
1.11k
  for (guint i = 0; i < images->len; i++) {
342
782
    FuFirmware *img = g_ptr_array_index(images, i);
343
782
    fu_byte_array_append_uint32(buf_hdr, fu_firmware_get_addr(img), G_BIG_ENDIAN);
344
782
    fu_byte_array_append_uint32(buf_hdr, fu_firmware_get_size(img), G_BIG_ENDIAN);
345
782
  }
346
336
  g_string_append_printf(str, "WACOM%u", images->len);
347
6.59k
  for (guint i = 0; i < buf_hdr->len; i++)
348
6.25k
    g_string_append_printf(str, "%02X", buf_hdr->data[i]);
349
336
  g_string_append_printf(str, "%02X\n", fu_wacom_usb_firmware_calc_checksum(buf_hdr));
350
351
  /* payload */
352
1.11k
  for (guint i = 0; i < images->len; i++) {
353
782
    FuFirmware *img = g_ptr_array_index(images, i);
354
782
    g_autoptr(GBytes) img_blob = NULL;
355
782
    g_autoptr(GByteArray) buf_img = g_byte_array_new();
356
357
    /* img header */
358
782
    g_string_append_printf(str, "WA%u", (guint)fu_firmware_get_idx(img) + 1);
359
782
    fu_byte_array_append_uint32(buf_img, fu_firmware_get_addr(img), G_BIG_ENDIAN);
360
3.91k
    for (guint j = 0; j < buf_img->len; j++)
361
3.12k
      g_string_append_printf(str, "%02X", buf_img->data[j]);
362
782
    g_string_append_printf(str, "%02X\n", fu_wacom_usb_firmware_calc_checksum(buf_img));
363
364
    /* srec */
365
782
    img_blob = fu_firmware_write(img, error);
366
782
    if (img_blob == NULL)
367
0
      return NULL;
368
782
    g_string_append_len(str,
369
782
            (const gchar *)g_bytes_get_data(img_blob, NULL),
370
782
            g_bytes_get_size(img_blob));
371
782
  }
372
373
  /* success */
374
336
  g_byte_array_append(buf, (const guint8 *)str->str, str->len);
375
336
  return g_steal_pointer(&buf);
376
336
}
377
378
static void
379
fu_wacom_usb_firmware_init(FuWacomUsbFirmware *self)
380
1.87k
{
381
1.87k
  fu_firmware_set_images_max(FU_FIRMWARE(self), 1024);
382
1.87k
  fu_firmware_set_size_max(FU_FIRMWARE(self), 256 * FU_MB);
383
1.87k
  fu_firmware_add_image_gtype(FU_FIRMWARE(self), FU_TYPE_FIRMWARE);
384
1.87k
  fu_firmware_add_image_gtype(FU_FIRMWARE(self), FU_TYPE_SREC_FIRMWARE);
385
1.87k
}
386
387
static void
388
fu_wacom_usb_firmware_class_init(FuWacomUsbFirmwareClass *klass)
389
1
{
390
1
  FuFirmwareClass *firmware_class = FU_FIRMWARE_CLASS(klass);
391
1
  firmware_class->validate = fu_wacom_usb_firmware_validate;
392
1
  firmware_class->parse = fu_wacom_usb_firmware_parse;
393
1
  firmware_class->write = fu_wacom_usb_firmware_write;
394
1
}
395
396
FuFirmware *
397
fu_wacom_usb_firmware_new(void)
398
0
{
399
0
  return FU_FIRMWARE(g_object_new(FU_TYPE_WACOM_USB_FIRMWARE, NULL));
400
0
}