Coverage Report

Created: 2026-01-09 07:21

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/fwupd/plugins/synaptics-mst/fu-synaptics-mst-firmware.c
Line
Count
Source
1
/*
2
 * Copyright 2021 Richard Hughes <richard@hughsie.com>
3
 *
4
 * SPDX-License-Identifier: LGPL-2.1-or-later
5
 */
6
7
#include "config.h"
8
9
#include "fu-synaptics-mst-common.h"
10
#include "fu-synaptics-mst-firmware.h"
11
12
struct _FuSynapticsMstFirmware {
13
  FuFirmware parent_instance;
14
  guint16 board_id;
15
  FuSynapticsMstFamily family;
16
};
17
18
364
G_DEFINE_TYPE(FuSynapticsMstFirmware, fu_synaptics_mst_firmware, FU_TYPE_FIRMWARE)
19
364
20
364
#define ADDR_CUSTOMER_ID_CARRERA 0x620E
21
19
#define ADDR_CUSTOMER_ID_CAYENNE 0x20E
22
40
#define ADDR_CUSTOMER_ID_TESLA   0x10E
23
164
#define ADDR_CONFIG_CARRERA  0x6200
24
164
#define ADDR_CONFIG_CAYENNE  0x200
25
164
#define ADDR_CONFIG_TESLA  0x100
26
27
guint16
28
fu_synaptics_mst_firmware_get_board_id(FuSynapticsMstFirmware *self)
29
0
{
30
0
  g_return_val_if_fail(FU_IS_SYNAPTICS_MST_FIRMWARE(self), 0);
31
0
  return self->board_id;
32
0
}
33
34
void
35
fu_synaptics_mst_firmware_set_family(FuSynapticsMstFirmware *self, FuSynapticsMstFamily family)
36
0
{
37
0
  g_return_if_fail(FU_IS_SYNAPTICS_MST_FIRMWARE(self));
38
0
  self->family = family;
39
0
}
40
41
static void
42
fu_synaptics_mst_firmware_export(FuFirmware *firmware,
43
         FuFirmwareExportFlags flags,
44
         XbBuilderNode *bn)
45
0
{
46
0
  FuSynapticsMstFirmware *self = FU_SYNAPTICS_MST_FIRMWARE(firmware);
47
0
  fu_xmlb_builder_insert_kx(bn, "board_id", self->board_id);
48
0
  fu_xmlb_builder_insert_kv(bn, "family", fu_synaptics_mst_family_to_string(self->family));
49
0
}
50
51
static gboolean
52
fu_synaptics_mst_firmware_detect_family(FuSynapticsMstFirmware *self,
53
          GInputStream *stream,
54
          gsize offset,
55
          GError **error)
56
164
{
57
164
  guint16 addrs[] = {ADDR_CONFIG_TESLA, ADDR_CONFIG_CAYENNE, ADDR_CONFIG_CARRERA};
58
316
  for (guint i = 0; i < G_N_ELEMENTS(addrs); i++) {
59
299
    g_autoptr(FuStructSynapticsFirmwareConfig) st = NULL;
60
299
    st = fu_struct_synaptics_firmware_config_parse_stream(stream,
61
299
                      offset + addrs[i],
62
299
                      error);
63
299
    if (st == NULL)
64
80
      return FALSE;
65
219
    if ((fu_struct_synaptics_firmware_config_get_magic1(st) & 0x80) &&
66
105
        (fu_struct_synaptics_firmware_config_get_magic2(st) & 0x80)) {
67
67
      self->family = fu_struct_synaptics_firmware_config_get_version(st) >> 4;
68
67
      return TRUE;
69
67
    }
70
219
  }
71
17
  g_set_error_literal(error,
72
17
          FWUPD_ERROR,
73
17
          FWUPD_ERROR_NOT_SUPPORTED,
74
17
          "unable to autodetect chip family");
75
17
  return FALSE;
76
164
}
77
78
static gboolean
79
fu_synaptics_mst_firmware_parse(FuFirmware *firmware,
80
        GInputStream *stream,
81
        FuFirmwareParseFlags flags,
82
        GError **error)
83
164
{
84
164
  FuSynapticsMstFirmware *self = FU_SYNAPTICS_MST_FIRMWARE(firmware);
85
164
  guint16 addr;
86
87
  /* if device family not specified by caller, try to get from firmware file */
88
164
  if (self->family == FU_SYNAPTICS_MST_FAMILY_UNKNOWN) {
89
164
    if (!fu_synaptics_mst_firmware_detect_family(self, stream, 0x0, error))
90
97
      return FALSE;
91
164
  }
92
93
67
  switch (self->family) {
94
15
  case FU_SYNAPTICS_MST_FAMILY_TESLA:
95
19
  case FU_SYNAPTICS_MST_FAMILY_LEAF:
96
22
  case FU_SYNAPTICS_MST_FAMILY_PANAMERA:
97
22
    addr = ADDR_CUSTOMER_ID_TESLA;
98
22
    break;
99
9
  case FU_SYNAPTICS_MST_FAMILY_CAYENNE:
100
10
  case FU_SYNAPTICS_MST_FAMILY_SPYDER:
101
10
    addr = ADDR_CUSTOMER_ID_CAYENNE;
102
10
    break;
103
15
  case FU_SYNAPTICS_MST_FAMILY_CARRERA:
104
15
    addr = ADDR_CUSTOMER_ID_CARRERA;
105
15
    break;
106
20
  default:
107
20
    g_set_error(error,
108
20
          FWUPD_ERROR,
109
20
          FWUPD_ERROR_NOT_SUPPORTED,
110
20
          "unsupported chip family %s",
111
20
          fu_synaptics_mst_family_to_string(self->family));
112
20
    return FALSE;
113
67
  }
114
47
  if (!fu_input_stream_read_u16(stream, addr, &self->board_id, G_BIG_ENDIAN, error))
115
11
    return FALSE;
116
117
  /* success */
118
36
  return TRUE;
119
47
}
120
121
static GByteArray *
122
fu_synaptics_mst_firmware_write(FuFirmware *firmware, GError **error)
123
36
{
124
36
  FuSynapticsMstFirmware *self = FU_SYNAPTICS_MST_FIRMWARE(firmware);
125
36
  g_autoptr(GByteArray) buf = g_byte_array_new();
126
36
  g_autoptr(GBytes) blob = NULL;
127
36
  guint16 addr;
128
129
36
  switch (self->family) {
130
12
  case FU_SYNAPTICS_MST_FAMILY_TESLA:
131
15
  case FU_SYNAPTICS_MST_FAMILY_LEAF:
132
18
  case FU_SYNAPTICS_MST_FAMILY_PANAMERA:
133
18
    addr = ADDR_CUSTOMER_ID_TESLA;
134
18
    break;
135
8
  case FU_SYNAPTICS_MST_FAMILY_CAYENNE:
136
9
  case FU_SYNAPTICS_MST_FAMILY_SPYDER:
137
9
    addr = ADDR_CUSTOMER_ID_CAYENNE;
138
9
    break;
139
9
  case FU_SYNAPTICS_MST_FAMILY_CARRERA:
140
9
    addr = ADDR_CUSTOMER_ID_CARRERA;
141
9
    break;
142
0
  default:
143
0
    g_set_error_literal(error,
144
0
            FWUPD_ERROR,
145
0
            FWUPD_ERROR_NOT_SUPPORTED,
146
0
            "unsupported chip family");
147
0
    return NULL;
148
36
  }
149
  /* assumed header */
150
36
  fu_byte_array_set_size(buf, addr + sizeof(guint16), 0x00);
151
36
  if (!fu_memwrite_uint16_safe(buf->data,
152
36
             buf->len,
153
36
             addr,
154
36
             fu_firmware_get_idx(firmware),
155
36
             G_BIG_ENDIAN,
156
36
             error))
157
0
    return NULL;
158
159
  /* payload */
160
36
  blob = fu_firmware_get_bytes_with_patches(firmware, error);
161
36
  if (blob == NULL)
162
36
    return NULL;
163
0
  fu_byte_array_append_bytes(buf, blob);
164
165
  /* success */
166
0
  return g_steal_pointer(&buf);
167
36
}
168
169
static gboolean
170
fu_synaptics_mst_firmware_build(FuFirmware *firmware, XbNode *n, GError **error)
171
0
{
172
0
  FuSynapticsMstFirmware *self = FU_SYNAPTICS_MST_FIRMWARE(firmware);
173
0
  guint64 tmp;
174
175
  /* optional properties */
176
0
  tmp = xb_node_query_text_as_uint(n, "board_id", NULL);
177
0
  if (tmp != G_MAXUINT64)
178
0
    self->board_id = tmp;
179
0
  tmp = xb_node_query_text_as_uint(n, "family", NULL);
180
0
  if (tmp != G_MAXUINT64)
181
0
    self->family = tmp;
182
183
  /* success */
184
0
  return TRUE;
185
0
}
186
187
static void
188
fu_synaptics_mst_firmware_init(FuSynapticsMstFirmware *self)
189
164
{
190
164
  self->family = FU_SYNAPTICS_MST_FAMILY_UNKNOWN;
191
164
  fu_firmware_add_flag(FU_FIRMWARE(self), FU_FIRMWARE_FLAG_NO_AUTO_DETECTION);
192
164
}
193
194
static void
195
fu_synaptics_mst_firmware_class_init(FuSynapticsMstFirmwareClass *klass)
196
1
{
197
1
  FuFirmwareClass *firmware_class = FU_FIRMWARE_CLASS(klass);
198
1
  firmware_class->parse = fu_synaptics_mst_firmware_parse;
199
1
  firmware_class->export = fu_synaptics_mst_firmware_export;
200
1
  firmware_class->write = fu_synaptics_mst_firmware_write;
201
1
  firmware_class->build = fu_synaptics_mst_firmware_build;
202
1
}
203
204
FuFirmware *
205
fu_synaptics_mst_firmware_new(void)
206
0
{
207
0
  return FU_FIRMWARE(g_object_new(FU_TYPE_SYNAPTICS_MST_FIRMWARE, NULL));
208
0
}