/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 | } |