-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathNuvoFlash.c
373 lines (340 loc) · 9.58 KB
/
NuvoFlash.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
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
//! gcc -Wall -I . -L . "%file%" -o "%name%" -lserialport
#include <getopt.h>
#include <libserialport.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <time.h>
#define PAGE_SIZE 128
#define MAX_PATH 260
typedef enum { APROM, LDROM, CONFIG } Mem;
bool quiet = false;
struct sp_port *port;
void usage() {
fputs("Usage: nuvoflash <options> [file.bin|hex_value]\n", stderr);
fputs("<mem> must be one of APROM, LDROM, CONFIG\n", stderr);
fputs("file.bin must be specified with -r and -w and APROM/LDROM mem types\n",
stderr);
fputs("hex_value must be specified with the -w option and CONFIG mem type\n",
stderr);
fputs(" -q/--quiet\treduce output\n", stderr);
fputs(" -r/--read <mem>\tread <mem>\n", stderr);
fputs(" -w/--write <mem>\twrite <mem>\n", stderr);
fputs(" -x/--massErase\t\tmass erase all flash memory\n", stderr);
fputs(" -p/--port <port>\tserial port (if omitted it will be automatically "
"selected)\n",
stderr);
exit(1);
}
Mem memFromString(const char *s) {
if (strcmp(s, "APROM") == 0)
return APROM;
else if (strcmp(s, "LDROM") == 0)
return LDROM;
else if (strcmp(s, "CONFIG") == 0)
return CONFIG;
else {
fprintf(stderr, "Invalid value '%s', must be APROM, LDROM or CONFIG\n", s);
usage();
}
return -1;
}
bool selectSerialPort(size_t len, char portName[len]) {
struct sp_port **port_list;
enum sp_return res = sp_list_ports(&port_list);
if (res != SP_OK)
return false;
for (int i = 0; port_list[i] != NULL; i++) {
int vid, pid;
res = sp_get_port_usb_vid_pid(port_list[i], &vid, &pid);
if (res != SP_OK)
continue;
if (pid == 0xc550 && vid == 0x1209) {
strncpy(portName, sp_get_port_name(port_list[i]), len);
if (!quiet)
fprintf(stderr, "Serial port automatically selected (%s)\n",
sp_get_port_description(port_list[i]));
return true;
}
}
sp_free_port_list(port_list);
return false;
}
bool readBlock(uint8_t mem, int address, size_t len, uint8_t buf[len]) {
uint8_t err, cmd[4] = {'R', mem, address >> 8, address};
sp_blocking_write(port, cmd, mem == 'C' ? 2 : 4, 500);
int nBytesRead = sp_blocking_read(port, &err, 1, 500);
if (nBytesRead != 1) {
fprintf(stderr, "Programmer is not responding\n");
return false;
}
if (err != 0) {
if (err == 255)
fputs("Target board nor responding\n", stderr);
else
fprintf(stderr, "Programmer returned error code %d\n", err);
return false;
}
nBytesRead = sp_blocking_read(port, buf, len, 500);
if (nBytesRead != len) {
fprintf(stderr, "Programmer is not responding\n");
return false;
}
return true;
}
bool writeBlock(uint8_t mem, int address, size_t len, const uint8_t buf[len]) {
uint8_t err, cmd[4] = {'W', mem, address >> 8, address};
sp_blocking_write(port, cmd, mem == 'C' ? 2 : 4, 500);
sp_blocking_write(port, buf, len, 500);
int nBytesRead = sp_blocking_read(port, &err, 1, 500);
if (nBytesRead != 1) {
fprintf(stderr, "Programmer is not responding\n");
return false;
}
if (err != 0) {
if (err == 255)
fputs("Target board nor responding\n", stderr);
else
fprintf(stderr, "Programmer returned error code %d\n", err);
return false;
}
return true;
}
void readROM(const char *filename, uint8_t mem, int size) {
uint8_t buf[PAGE_SIZE];
FILE *f = fopen(filename, "wb");
if (f == NULL) {
fprintf(stderr, "Cannot write to file %s\n", filename);
exit(1);
}
for (int i = 0; i < size; i += PAGE_SIZE) {
if (!readBlock(mem, i, PAGE_SIZE, buf)) {
fclose(f);
exit(1);
}
if (!quiet && isatty(fileno(stdout)))
printf("Read: %5d\r", i);
fwrite(buf, 1, PAGE_SIZE, f);
}
fclose(f);
}
void writeROM(const char *filename, uint8_t mem) {
uint8_t buf[PAGE_SIZE], buf1[PAGE_SIZE];
FILE *f = fopen(filename, "rb");
if (f == NULL) {
fprintf(stderr, "Cannot read from file %s\n", filename);
exit(1);
}
for (int verify = 0; verify <= 1; verify++) {
fseek(f, 0, SEEK_SET);
for (int i = 0; i < 18 * 1024; i += PAGE_SIZE) {
memset(buf, 0xFF, sizeof buf);
int n = fread(buf, 1, PAGE_SIZE, f);
if (n == 0)
break;
if (!quiet && isatty(fileno(stdout)))
printf("%s: %5d\r", verify ? "Verify" : "Write", i);
bool ok;
if (verify) {
ok = readBlock(mem, i, PAGE_SIZE, buf1);
if (ok)
if (0 != memcmp(buf, buf1, PAGE_SIZE)) {
fputs("Verify failed\n", stderr);
exit(3);
}
} else
ok = writeBlock(mem, i, PAGE_SIZE, buf);
if (!ok) {
fclose(f);
exit(1);
}
if (n != PAGE_SIZE)
break;
}
}
fclose(f);
}
void readAPROM(const char *filename, int size) { readROM(filename, 'A', size); }
void readLDROM(const char *filename, int size) { readROM(filename, 'L', size); }
void writeAPROM(const char *filename, int apromSize) {
struct stat st;
stat(filename, &st);
if (st.st_size > apromSize) {
fprintf(stderr, "File is too big, APROM size is %d\n", apromSize);
exit(1);
}
writeROM(filename, 'A');
}
void writeLDROM(const char *filename, int ldromSize) {
struct stat st;
stat(filename, &st);
if (st.st_size > ldromSize) {
fprintf(stderr, "File is too big, LDROM size is %d\n", ldromSize);
exit(1);
}
writeROM(filename, 'L');
}
void readConfig(uint8_t cfg[]) {
if (!readBlock('C', 0, 5, cfg))
exit(2);
}
void printConfig(uint8_t cfg[]) {
for (int i = 0; i < 5; i++)
printf("%02X", cfg[i]);
putchar('\n');
}
void writeConfig(const uint8_t cfg[]) {
uint8_t buf[5];
if (!writeBlock('C', 0, 5, cfg))
exit(2);
readConfig(buf);
if (0 != memcmp(cfg, buf, 5)) {
fputs("Verify failed\n", stderr);
exit(3);
}
}
void massErase() {
uint8_t err;
sp_blocking_write(port, "X", 1, 500);
if (sp_blocking_read(port, &err, 1, 500) != 1) {
fprintf(stderr, "Programmer is not responding\n");
exit(1);
}
if (err != 0) {
if (err == 255)
fputs("Target board nor responding\n", stderr);
else
fprintf(stderr, "Programmer returned error code %d\n", err);
exit(1);
}
}
int main(int argc, char *argv[]) {
Mem mem;
char opt;
int opt_index, ldromSize = 0, apromSize = 18 * 1024;
uint8_t buf[PAGE_SIZE];
char portName[20] = "";
bool portOpt = false, readOpt = false, writeOpt = false, massEraseOpt = false;
uint8_t cfg[5];
static struct option long_options[] = {
{"quiet", no_argument, NULL, 'q'},
{"port", required_argument, NULL, 'p'},
{"read", required_argument, NULL, 'r'},
{"write", required_argument, NULL, 'w'},
{"massErase", no_argument, NULL, 'x'},
{0, 0, 0, 0}};
clock_t begin = clock();
while ((opt = getopt_long(argc, argv, "qp:r:w:x", long_options,
&opt_index)) != -1) {
switch (opt) {
case 'q':
quiet = true;
break;
case 'p':
strncpy(portName, optarg, sizeof portName - 1);
break;
case 'r':
readOpt = true;
mem = memFromString(optarg);
break;
case 'w':
writeOpt = true;
mem = memFromString(optarg);
break;
case 'x':
massEraseOpt = true;
break;
default:
usage();
}
}
if (!readOpt && !writeOpt && !massEraseOpt) {
fputs("Exactly one of read, write, erase must be specified\n", stderr);
usage();
} else if ((readOpt && (writeOpt || massEraseOpt)) ||
(writeOpt && massEraseOpt)) {
fputs("Only one of read, write, erase is allowed\n", stderr);
usage();
}
if (!massEraseOpt && (mem != CONFIG || writeOpt) && argc != 1 &&
optind != argc - 1) {
fputs("Missing arguments\n", stderr);
usage();
}
if (!portOpt) {
if (!selectSerialPort(sizeof portName - 1, portName)) {
fputs("Serial port not found, try using the -p/--port option\n", stderr);
exit(1);
}
}
enum sp_return res = sp_get_port_by_name(portName, &port);
if (res != SP_OK) {
fputs("The serial port does not exist, exiting\n", stderr);
exit(1);
}
res = sp_open(port, SP_MODE_READ_WRITE);
if (res != SP_OK) {
fputs("Cannot open serial port, exiting\n", stderr);
exit(1);
}
res = sp_set_baudrate(port, 115200);
if (res != SP_OK) {
fputs("Cannot set baud rate, exiting\n", stderr);
exit(1);
}
readConfig(cfg);
ldromSize = (7 - (cfg[1] & 7)) * 1024;
if (ldromSize > 4 * 1024)
ldromSize = 4 * 1024;
apromSize -= ldromSize;
char *p, *end;
unsigned long long l = 0;
if (readOpt)
switch (mem) {
case CONFIG:
printConfig(cfg);
break;
case APROM:
readAPROM(argv[argc - 1], apromSize);
break;
case LDROM:
readLDROM(argv[argc - 1], ldromSize);
}
else if (writeOpt)
switch (mem) {
case CONFIG:
p = argv[argc - 1];
if (strlen(p) != 10) {
fprintf(stderr,
"config bytes string length wrong (%llu insted of 10)\n",
strlen(p));
exit(1);
}
l = strtoull(p, &end, 16);
if (*end != 0) {
fprintf(stderr, "config bytes string contains invalid characters\n");
exit(1);
}
for (int i = 4; i >= 0; i--) {
buf[i] = l;
l >>= 8;
}
writeConfig(buf);
break;
case APROM:
writeAPROM(argv[argc - 1], apromSize);
break;
case LDROM:
writeLDROM(argv[argc - 1], ldromSize);
}
else if (massEraseOpt)
massErase();
sp_close(port);
if (!quiet)
fprintf(stderr, "Operation completed in %.2f seconds\n",
((double)(clock() - begin)) / CLOCKS_PER_SEC);
return 0;
}