Coverage Report

Created: 2026-08-15 06:58

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