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