-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathmtkeepmgr.c
297 lines (254 loc) · 8.51 KB
/
mtkeepmgr.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
/**
* MediaTek EEPROM management utility
*
* Copyright (c) 2016-2021, Sergey Ryazanov <[email protected]>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <libgen.h>
#include <unistd.h>
#include <stdint.h>
#include <endian.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "mtkeepmgr.h"
extern struct chip_desc *__start___chips[];
extern struct chip_desc *__stop___chips;
#define for_each_chip(__chip, __i) \
for (__i = 0; __i < &__stop___chips - __start___chips; ++__i) \
if ((__chip = __start___chips[i])) /* to skip possible padding */
extern const struct connector_desc con_file;
extern const struct connector_desc con_usb;
/* The main utility execution context */
static struct main_ctx __mc;
uint16_t eep_read_word(struct main_ctx *mc, const unsigned offset)
{
uint16_t val;
if (offset >= mc->eep_len)
return 0xffff;
memcpy(&val, &mc->eep_buf[offset], sizeof(val));
return le16toh(val);
}
static int act_eep_dump(struct main_ctx *mc, int argc, char *argv[])
{
uint16_t chipid, version;
struct chip_desc *chip = NULL;
int i;
printf("[EEPROM identification]\n");
chipid = eep_read_word(mc, E_CHIPID);
printf(" ChipID : %04Xh\n", chipid);
version = eep_read_word(mc, E_VERSION);
printf(" Version : %u.%u\n",
FIELD_GET(E_VERSION_VERSION, version),
FIELD_GET(E_VERSION_REVISION, version));
for_each_chip(chip, i)
if (chip->chipid == chipid)
break;
if (!chip || chip->chipid != chipid) {
fprintf(stderr, "EEPROM dump is for unknown or unsupported chip (chipid:0x%04x)\n",
chipid);
return -1;
}
printf(" Chip : %s\n", chip->name);
printf("\n");
return chip->parse_func(mc);
}
static int act_eep_save(struct main_ctx *mc, int argc, char *argv[])
{
FILE *fp;
const uint8_t *buf = mc->eep_buf;
int eep_len = mc->eep_len;
size_t res;
if (argc < 1) {
fprintf(stderr, "Output file for EEPROM saving is not specified, aborting\n");
return -EINVAL;
}
fp = fopen(argv[0], "wb");
if (!fp) {
fprintf(stderr, "Unable to open output file for writing: %s\n",
strerror(errno));
return -errno;
}
res = fwrite(buf, 1, eep_len, fp);
if (res != eep_len)
fprintf(stderr, "Unable to save whole EEPROM contents: %s\n",
strerror(errno));
fclose(fp);
return res == eep_len ? 0 : -EIO;
}
static const struct action {
const char * const name;
int (*func)(struct main_ctx *mc, int argc, char *argv[]);
} actions[] = {
{
.name = "dump",
.func = act_eep_dump,
}, {
.name = "save",
.func = act_eep_save,
}
};
#define CON_USAGE_FILE "-F <eepdump>"
#ifdef CONFIG_CON_USB
#define CON_USAGE_USB " | -U <dev-sel>"
#define CON_OPTSTR_USB "U:"
#else
#define CON_USAGE_USB ""
#define CON_OPTSTR_USB ""
#endif
#define CON_OPTSTR "F:" CON_OPTSTR_USB
#if defined(CONFIG_CON_USB)
#define CON_USAGE "{" CON_USAGE_FILE CON_USAGE_USB "}"
#else
#define CON_USAGE CON_USAGE_FILE
#endif
static void usage_chips(void)
{
struct chip_desc *chip = NULL;
int i;
for_each_chip(chip, i) {
printf("%s%s", !i ? "" : ", ", chip->name);
}
}
static void usage(const char *name)
{
printf(
"MediaTek EEPROM management utility v0.2\n"
"Copyright (c) 2016-2021, Sergey Ryazanov <[email protected]>\n"
"\n"
"Usage:\n"
" %s [-h] " CON_USAGE " [<action> [<actarg>]]\n"
"\n"
"Options:\n"
" -F <eepdump>\n"
" Read EEPROM dump from <eepdump> file.\n"
#ifdef CONFIG_CON_USB
" -U <dev-sel>\n"
" Work with USB device, which is specified by a selector <dev-sel>.\n"
" Utiltity supported a few types of selection rules.\n"
" To select device by a USB bus number and corresponding address on the\n"
" bus use '<busnum>:<devaddr>' format, where <busnum> and <devaddr>\n"
" should be specified in the decimal form (e.g. '1:193').\n"
" To select first device with a specific IDs, use <VID>:<PID>, where\n"
" <VID> and <PID> should be specified in the cannonical 4 symbol hex\n"
" form (e.g. '148f:7610'). This form could be used not only to narrow\n"
" device selection range, but also to force device selection, when\n"
" IDs of a new device is not yet embedded to the utility table of\n"
" supported devices.\n"
" To be absolutely specific, you could select a device by its path\n"
" (i.e. sequence of HUB ports from root to device) on a bus. Use this\n"
" format to utilize path selector:\n"
" '<busnum>/<hub1portnum>[/<hub2port>[/<hub3port>[...]]][/]'. Please\n"
" note optional leading path slash. If the leading slash is specified\n"
" then path treated as a required prefix of a full device path. If the\n"
" leading slash is omited, then path treated as an exact full device\n"
" path. So you could specify full device path or only a path to an USB\n"
" that you like to use to connect your device.\n"
" Several selectors could be specified at once by concatenating them\n"
" with comma (e.g. <busnum>:<devaddr>,<VID>:<PID>). Such format could\n"
" be used to force yet unknown device usage and in the same time select\n"
" one specific device of multiple connected to a host. NB: device\n"
" address and device path selectors are mutually exclusive and should\n"
" not be used together.\n"
" There is a special keyword 'any'. If it specified, then the utility\n"
" will open first device with a known VID/PID pair. This is useful\n"
" when you have only one device connected to the host and you do not\n"
" want to type a longer option argument.\n"
#endif
" -h Print this help\n"
" <action> Optional argument, which specifies the <action> that should be\n"
" performed (see actions list below). If no action is specified, then\n"
" the 'dump' action is performed by default.\n"
" <actarg> Action argument if the action accepts any (see details below in the\n"
" detailed actions list).\n"
"\n"
"Available actions:\n"
" dump Read & parse the EEPROM content and then dump parsed results to the\n"
" terminal (this is the default action).\n"
" save <file>\n"
" Save fetched raw EEPROM content to the file <file>.\n"
"\n",
name
);
printf("Supported EEPROM formats (chips): ");
usage_chips();
printf("\n\n");
}
int main(int argc, char *argv[])
{
const char *appname = basename(argv[0]);
struct main_ctx *mc = &__mc;
const struct action *act = NULL;
char *con_arg = NULL;
int i, opt, ret = -EINVAL;
if (argc <= 1) {
usage(appname);
return EXIT_SUCCESS;
}
while ((opt = getopt(argc, argv, CON_OPTSTR "h")) != -1) {
switch (opt) {
case 'F':
mc->con = &con_file;
con_arg = optarg;
break;
#ifdef CONFIG_CON_USB
case 'U':
mc->con = &con_usb;
con_arg = optarg;
break;
#endif
case 'h':
usage(appname);
return EXIT_SUCCESS;
default:
return EXIT_FAILURE;
}
}
if (!mc->con) {
fprintf(stderr, "Connector (data source) was not specified\n");
goto exit;
}
if (optind >= argc) {
act = &actions[0]; /* Select first action by default */
} else {
for (i = 0; i < ARRAY_SIZE(actions); ++i) {
if (strcasecmp(argv[optind], actions[i].name) != 0)
continue;
act = &actions[i];
break;
}
if (!act) {
fprintf(stderr, "Unknown action -- %s\n", argv[optind]);
goto exit;
}
optind++;
}
mc->con_priv = malloc(mc->con->priv_sz);
if (!mc->con_priv) {
fprintf(stderr, "Unable to allocate memory for a connector private data\n");
goto exit;
}
ret = mc->con->init(mc, con_arg);
if (ret)
goto exit;
ret = act->func(mc, argc - optind, argv + optind);
mc->con->clean(mc);
exit:
free(mc->con_priv);
return ret ? EXIT_FAILURE : EXIT_SUCCESS;
}