Coverage Report

Created: 2026-10-28 07:47

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/fwupd/plugins/pixart-rf/fu-pixart-rf-firmware.c
Line
Count
Source
1
/*
2
 * Copyright 2020 Jimmy Yu <Jimmy_yu@pixart.com>
3
 *
4
 * SPDX-License-Identifier: LGPL-2.1-or-later
5
 */
6
7
#include "config.h"
8
9
#include "fu-pixart-rf-common.h"
10
#include "fu-pixart-rf-firmware.h"
11
12
784
#define PIXART_RF_FW_HEADER_SIZE       32 /* bytes */
13
653
#define PIXART_RF_FW_HEADER_TAG_OFFSET 24
14
/* The hpac header is start from 821st byte from the end */
15
446
#define PIXART_RF_FW_HEADER_HPAC_POS_FROM_END       821
16
87
#define PIXART_RF_FW_HEADER_HPAC_VERSION_POS_FROM_END 823
17
18
627
#define PIXART_RF_FW_HEADER_MAGIC 0x55AA55AA55AA55AA
19
20
struct _FuPixartRfFirmware {
21
  FuFirmware parent_instance;
22
  gchar *model_name;
23
  gboolean is_hpac;
24
};
25
26
401
G_DEFINE_TYPE(FuPixartRfFirmware, fu_pixart_rf_firmware, FU_TYPE_FIRMWARE)
27
401
28
401
const gchar *
29
401
fu_pixart_rf_firmware_get_model_name(FuPixartRfFirmware *self)
30
401
{
31
0
  g_return_val_if_fail(FU_IS_PIXART_RF_FIRMWARE(self), NULL);
32
0
  return self->model_name;
33
0
}
34
35
static void
36
fu_pixart_rf_firmware_export(FuFirmware *firmware, FuFirmwareExportFlags flags, XbBuilderNode *bn)
37
0
{
38
0
  FuPixartRfFirmware *self = FU_PIXART_RF_FIRMWARE(firmware);
39
0
  fu_xmlb_builder_insert_kv(bn, "model_name", self->model_name);
40
0
}
41
42
static gboolean
43
fu_pixart_rf_firmware_validate(FuFirmware *firmware,
44
             FuInputStream *stream,
45
             gsize offset,
46
             GError **error)
47
399
{
48
399
  guint64 magic = 0;
49
399
  FuPixartRfFirmware *self = FU_PIXART_RF_FIRMWARE(firmware);
50
399
  gsize streamsz = 0;
51
52
  /* is a footer, in normal bin file, the header is starts from the 32nd byte from the end. */
53
399
  if (!fu_input_stream_size(stream, &streamsz, error))
54
0
    return FALSE;
55
399
  if (streamsz < PIXART_RF_FW_HEADER_SIZE) {
56
14
    g_set_error_literal(error,
57
14
            FWUPD_ERROR,
58
14
            FWUPD_ERROR_INVALID_FILE,
59
14
            "stream was too small");
60
14
    return FALSE;
61
14
  }
62
385
  if (!fu_input_stream_read_u64(stream,
63
385
              streamsz - PIXART_RF_FW_HEADER_SIZE +
64
385
            PIXART_RF_FW_HEADER_TAG_OFFSET,
65
385
              &magic,
66
385
              G_BIG_ENDIAN,
67
385
              error)) {
68
0
    g_prefix_error_literal(error, "failed to read magic: ");
69
0
    return FALSE;
70
0
  }
71
385
  if (magic != PIXART_RF_FW_HEADER_MAGIC) {
72
    /* if the magic number is not found, then start from the 821st byte from the end for
73
     * HPAC header */
74
260
    if (streamsz < PIXART_RF_FW_HEADER_HPAC_POS_FROM_END) {
75
117
      g_set_error_literal(error,
76
117
              FWUPD_ERROR,
77
117
              FWUPD_ERROR_INVALID_FILE,
78
117
              "stream too small for HPAC header");
79
117
      return FALSE;
80
117
    }
81
143
    if (!fu_input_stream_read_u64(stream,
82
143
                streamsz - PIXART_RF_FW_HEADER_HPAC_POS_FROM_END +
83
143
              PIXART_RF_FW_HEADER_TAG_OFFSET,
84
143
                &magic,
85
143
                G_BIG_ENDIAN,
86
143
                error)) {
87
0
      g_prefix_error_literal(error, "failed to read magic: ");
88
0
      return FALSE;
89
0
    }
90
143
    if (magic != PIXART_RF_FW_HEADER_MAGIC) {
91
99
      g_set_error(error,
92
99
            FWUPD_ERROR,
93
99
            FWUPD_ERROR_INVALID_FILE,
94
99
            "invalid magic, expected 0x%08X got 0x%08X",
95
99
            (guint32)PIXART_RF_FW_HEADER_MAGIC,
96
99
            (guint32)magic);
97
99
      return FALSE;
98
99
    }
99
44
    self->is_hpac = TRUE;
100
44
  }
101
102
  /* success */
103
169
  return TRUE;
104
385
}
105
106
static gboolean
107
fu_pixart_rf_firmware_parse(FuFirmware *firmware,
108
          FuInputStream *stream,
109
          FuFirmwareParseFlags flags,
110
          GError **error)
111
169
{
112
169
  FuPixartRfFirmware *self = FU_PIXART_RF_FIRMWARE(firmware);
113
169
  gsize streamsz = 0;
114
169
  guint32 version_raw = 0;
115
169
  guint8 fw_header[PIXART_RF_FW_HEADER_SIZE] = {0};
116
169
  guint8 model_name[FU_PIXART_RF_DEVICE_MODEL_NAME_LEN] = {0x0};
117
169
  guint16 hpac_ver = 0;
118
169
  g_autofree gchar *version = NULL;
119
120
  /* get fw header from buf */
121
169
  if (!fu_input_stream_size(stream, &streamsz, error))
122
0
    return FALSE;
123
169
  if (fu_pixart_rf_firmware_is_hpac(self)) {
124
44
    if (streamsz < PIXART_RF_FW_HEADER_HPAC_VERSION_POS_FROM_END) {
125
1
      g_set_error_literal(error,
126
1
              FWUPD_ERROR,
127
1
              FWUPD_ERROR_INVALID_DATA,
128
1
              "image is too small");
129
1
      return FALSE;
130
1
    }
131
43
    if (!fu_input_stream_read_safe(stream,
132
43
                 fw_header,
133
43
                 sizeof(fw_header),
134
43
                 0x0,
135
43
                 streamsz - PIXART_RF_FW_HEADER_HPAC_POS_FROM_END,
136
43
                 sizeof(fw_header),
137
43
                 error)) {
138
0
      g_prefix_error_literal(error, "failed to read fw header: ");
139
0
      return FALSE;
140
0
    }
141
125
  } else {
142
125
    if (streamsz < sizeof(fw_header)) {
143
0
      g_set_error_literal(error,
144
0
              FWUPD_ERROR,
145
0
              FWUPD_ERROR_INVALID_DATA,
146
0
              "image is too small");
147
0
      return FALSE;
148
0
    }
149
125
    if (!fu_input_stream_read_safe(stream,
150
125
                 fw_header,
151
125
                 sizeof(fw_header),
152
125
                 0x0,
153
125
                 streamsz - sizeof(fw_header),
154
125
                 sizeof(fw_header),
155
125
                 error)) {
156
0
      g_prefix_error_literal(error, "failed to read fw header: ");
157
0
      return FALSE;
158
0
    }
159
125
  }
160
161
168
  fu_dump_raw(G_LOG_DOMAIN, "fw header", fw_header, sizeof(fw_header));
162
163
  /* set fw version */
164
168
  if (fu_pixart_rf_firmware_is_hpac(self)) {
165
43
    if (!fu_input_stream_read_u16(stream,
166
43
                streamsz -
167
43
              PIXART_RF_FW_HEADER_HPAC_VERSION_POS_FROM_END,
168
43
                &hpac_ver,
169
43
                G_BIG_ENDIAN,
170
43
                error))
171
0
      return FALSE;
172
173
43
    version_raw = hpac_ver;
174
43
    version = fu_pixart_rf_hpac_version_info_parse(hpac_ver);
175
125
  } else {
176
125
    version_raw = (((guint32)(fw_header[0] - '0')) << 16) +
177
125
            (((guint32)(fw_header[2] - '0')) << 8) +
178
125
            (guint32)(fw_header[4] - '0');
179
125
    version = fu_version_from_uint32(version_raw, FWUPD_VERSION_FORMAT_DELL_BIOS);
180
125
  }
181
168
  fu_firmware_set_version_raw(firmware, version_raw);
182
168
  fu_firmware_set_version(firmware, version); /* nocheck:set-version */
183
184
  /* set fw model name */
185
168
  if (!fu_memcpy_safe(model_name,
186
168
          sizeof(model_name),
187
168
          0x0,
188
168
          fw_header,
189
168
          sizeof(fw_header),
190
168
          0x05,
191
168
          sizeof(model_name),
192
168
          error)) {
193
0
    g_prefix_error_literal(error, "failed to get fw model name: ");
194
0
    return FALSE;
195
0
  }
196
168
  self->model_name = fu_strsafe((const gchar *)model_name, sizeof(model_name));
197
198
  /* success */
199
168
  return TRUE;
200
168
}
201
202
static gboolean
203
fu_pixart_rf_firmware_build(FuFirmware *firmware, XbNode *n, GError **error)
204
0
{
205
0
  FuPixartRfFirmware *self = FU_PIXART_RF_FIRMWARE(firmware);
206
0
  const gchar *tmp;
207
208
  /* optional properties */
209
0
  tmp = xb_node_query_text(n, "model_name", NULL);
210
0
  if (tmp != NULL)
211
0
    self->model_name = g_strdup(tmp);
212
213
  /* success */
214
0
  return TRUE;
215
0
}
216
217
static GByteArray *
218
fu_pixart_rf_firmware_write(FuFirmware *firmware, GError **error)
219
168
{
220
168
  FuPixartRfFirmware *self = FU_PIXART_RF_FIRMWARE(firmware);
221
168
  guint8 fw_header[PIXART_RF_FW_HEADER_SIZE] = {0x0};
222
168
  guint64 version_raw = fu_firmware_get_version_raw(firmware);
223
168
  g_autoptr(GByteArray) buf = NULL;
224
168
  g_autoptr(GBytes) blob = NULL;
225
168
  const guint8 tag[] = {
226
168
      0x55,
227
168
      0xAA,
228
168
      0x55,
229
168
      0xAA,
230
168
      0x55,
231
168
      0xAA,
232
168
      0x55,
233
168
      0xAA,
234
168
  };
235
236
  /* data first */
237
168
  blob = fu_firmware_get_bytes_with_patches(firmware, error);
238
168
  if (blob == NULL)
239
0
    return NULL;
240
241
168
  if (fu_pixart_rf_firmware_is_hpac(self)) {
242
43
    buf = g_byte_array_sized_new(g_bytes_get_size(blob));
243
43
    fu_byte_array_append_bytes(buf, blob);
244
43
    return g_steal_pointer(&buf);
245
43
  }
246
247
125
  buf = g_byte_array_sized_new(g_bytes_get_size(blob) + sizeof(fw_header));
248
125
  fu_byte_array_append_bytes(buf, blob);
249
250
  /* footer */
251
125
  if (!fu_memcpy_safe(fw_header,
252
125
          sizeof(fw_header),
253
125
          PIXART_RF_FW_HEADER_TAG_OFFSET, /* dst */
254
125
          tag,
255
125
          sizeof(tag),
256
125
          0x0, /* src */
257
125
          sizeof(tag),
258
125
          error))
259
0
    return NULL;
260
125
  fw_header[0] = ((version_raw >> 16) & 0xff) + '0';
261
125
  fw_header[1] = '.';
262
125
  fw_header[2] = ((version_raw >> 8) & 0xff) + '0';
263
125
  fw_header[3] = '.';
264
125
  fw_header[4] = ((version_raw >> 0) & 0xff) + '0';
265
125
  if (!g_ascii_isdigit(fw_header[0]) || !g_ascii_isdigit(fw_header[2]) ||
266
87
      !g_ascii_isdigit(fw_header[4])) {
267
87
    g_set_error(error,
268
87
          FWUPD_ERROR,
269
87
          FWUPD_ERROR_NOT_SUPPORTED,
270
87
          "cannot write invalid version number 0x%x",
271
87
          (guint)version_raw);
272
87
    return NULL;
273
87
  }
274
38
  if (self->model_name != NULL) {
275
37
    gsize model_namesz =
276
37
        MIN(strlen(self->model_name), FU_PIXART_RF_DEVICE_MODEL_NAME_LEN);
277
37
    if (!fu_memcpy_safe(fw_header,
278
37
            sizeof(fw_header),
279
37
            0x05, /* dst */
280
37
            (const guint8 *)self->model_name,
281
37
            model_namesz,
282
37
            0x0, /* src */
283
37
            model_namesz,
284
37
            error)) {
285
0
      g_prefix_error_literal(error, "failed to get fw model name: ");
286
0
      return NULL;
287
0
    }
288
37
  }
289
290
38
  g_byte_array_append(buf, fw_header, sizeof(fw_header));
291
38
  return g_steal_pointer(&buf);
292
38
}
293
294
static void
295
fu_pixart_rf_firmware_init(FuPixartRfFirmware *self)
296
399
{
297
399
}
298
299
static void
300
fu_pixart_rf_firmware_finalize(GObject *object)
301
399
{
302
399
  FuPixartRfFirmware *self = FU_PIXART_RF_FIRMWARE(object);
303
399
  g_free(self->model_name);
304
399
  G_OBJECT_CLASS(fu_pixart_rf_firmware_parent_class)->finalize(object);
305
399
}
306
307
static void
308
fu_pixart_rf_firmware_class_init(FuPixartRfFirmwareClass *klass)
309
1
{
310
1
  GObjectClass *object_class = G_OBJECT_CLASS(klass);
311
1
  FuFirmwareClass *firmware_class = FU_FIRMWARE_CLASS(klass);
312
1
  object_class->finalize = fu_pixart_rf_firmware_finalize;
313
1
  firmware_class->validate = fu_pixart_rf_firmware_validate;
314
1
  firmware_class->parse = fu_pixart_rf_firmware_parse;
315
1
  firmware_class->build = fu_pixart_rf_firmware_build;
316
1
  firmware_class->write = fu_pixart_rf_firmware_write;
317
1
  firmware_class->export = fu_pixart_rf_firmware_export;
318
1
  fu_firmware_set_size_max(firmware_class, 16 * FU_MB);
319
1
}
320
321
FuFirmware *
322
fu_pixart_rf_firmware_new(void)
323
0
{
324
0
  return FU_FIRMWARE(g_object_new(FU_TYPE_PIXART_RF_FIRMWARE, NULL));
325
0
}
326
327
gboolean
328
fu_pixart_rf_firmware_is_hpac(FuPixartRfFirmware *self)
329
505
{
330
505
  return self->is_hpac;
331
505
}