Root/
1 | /* |
2 | * lpc111x-isp/lpc111x.c - LPC111x/LPC11Cxx ISP programmer |
3 | * |
4 | * Written 2012 by Werner Almesberger |
5 | * Copyright 2012 Werner Almesberger |
6 | * |
7 | * This program is free software; you can redistribute it and/or modify |
8 | * it under the terms of the GNU General Public License as published by |
9 | * the Free Software Foundation; either version 2 of the License, or |
10 | * (at your option) any later version. |
11 | */ |
12 | |
13 | |
14 | #define _GNU_SOURCE /* for vasprintf */ |
15 | #include <stdarg.h> |
16 | #include <stdlib.h> |
17 | #include <stdio.h> |
18 | #include <unistd.h> |
19 | #include <string.h> |
20 | #include <strings.h> /* for strcasecmp, strncasecmp */ |
21 | #include <alloca.h> |
22 | #include <assert.h> |
23 | #include <sys/types.h> |
24 | |
25 | #include <ubb/ubb.h> |
26 | #include <ubb/swuart.h> |
27 | |
28 | |
29 | #define BPS 115200 |
30 | |
31 | #define MAX_BUF 10000 /* receive buffer */ |
32 | |
33 | #define AUTOBAUD_TRIES 10 |
34 | #define SYNC "Synchronized" |
35 | |
36 | #define MAX_RECORD 45 /* data bytes per uuencoded record */ |
37 | |
38 | /* |
39 | * UM 26.4.8 pg 416 says 0x1000017C to 0x1000025B and the last 256+32 bytes |
40 | * of the RAM are taken. |
41 | * |
42 | * UM 26.5.4 pg 418 hints 0x10000300 is a good place. |
43 | */ |
44 | |
45 | #define RAM_BUFFER 0x10000300 |
46 | #define SECTOR 4096 |
47 | #define PAGE 256 |
48 | |
49 | |
50 | enum { |
51 | pin_rxd, /* RXD pin on the device (PIO1_6) */ |
52 | pin_txd, /* TXD pin on the device (PIO1_7) */ |
53 | pin_nisp, /* nISP: ISP mode selection (PIO0_1) */ |
54 | pin_nreset, /* nRESET pin (PIO0_0) */ |
55 | pin_end /* last value */ |
56 | }; |
57 | |
58 | static struct pin { |
59 | const char *name; |
60 | const char *alt_name; |
61 | uint32_t pin; |
62 | } pins[] = { |
63 | [pin_rxd] = { "RXD", "P1_6", UBB_DAT2 }, |
64 | [pin_txd] = { "TXD", "P1_7", UBB_DAT3 }, |
65 | [pin_nisp] = { "nISP", "P0_1", UBB_DAT1 }, |
66 | [pin_nreset] = { "nRESET", "P0_0", UBB_CMD }, |
67 | [pin_end] = { NULL } |
68 | }; |
69 | |
70 | static const struct signal { |
71 | const char *name; |
72 | uint32_t pin; |
73 | } signals[] = { |
74 | { "CMD", UBB_CMD }, |
75 | { "CLK", UBB_CLK }, |
76 | { "DAT0", UBB_DAT0 }, |
77 | { "DAT1", UBB_DAT1 }, |
78 | { "DAT2", UBB_DAT2 }, |
79 | { "DAT3", UBB_DAT3 }, |
80 | { NULL } |
81 | }; |
82 | |
83 | static int verbose = 0; |
84 | static int quiet = 0; |
85 | |
86 | |
87 | /* ----- Debugging and tracing --------------------------------------------- */ |
88 | |
89 | |
90 | static void trace(const char *label, const uint8_t *s, int len) |
91 | { |
92 | const uint8_t *end = s+len; |
93 | |
94 | if (label) |
95 | fprintf(stderr, "%s ", label); |
96 | for (end = s+len; s != end; s++) { |
97 | if (*s >= ' ' && *s <= '~') |
98 | fprintf(stderr, "%c", *s); |
99 | else if (*s == 10) |
100 | fprintf(stderr, "\\n"); |
101 | else if (*s == 13) |
102 | fprintf(stderr, "\\r"); |
103 | else |
104 | fprintf(stderr, "\\%02o", *s); |
105 | } |
106 | fprintf(stderr, "\n"); |
107 | } |
108 | |
109 | |
110 | static void trace_out(const void *s, int len) |
111 | { |
112 | trace(">>>", s, len); |
113 | } |
114 | |
115 | |
116 | static void trace_in(const void *s, int len) |
117 | { |
118 | trace("<<<", s, len); |
119 | } |
120 | |
121 | |
122 | /* ----- Dialog functions -------------------------------------------------- */ |
123 | |
124 | |
125 | static const char *dialog_fixed(int idle, const char *msg) |
126 | { |
127 | static char *res = NULL; |
128 | int msg_len = strlen(msg); |
129 | char *tx_buf = alloca(msg_len+3); |
130 | char *rx_buf = alloca(MAX_BUF); |
131 | char *s, *t; |
132 | int got; |
133 | |
134 | memcpy(tx_buf, msg, msg_len); |
135 | memcpy(tx_buf+msg_len, "\r\n", 2); |
136 | |
137 | if (verbose) |
138 | trace_out(tx_buf, msg_len+2); |
139 | got = swuart_trx(tx_buf, msg_len+2, rx_buf, MAX_BUF, idle, idle); |
140 | if (verbose) |
141 | trace_in(rx_buf, got); |
142 | |
143 | if (got < msg_len+2) { |
144 | fprintf(stderr, "response too short for echo\n"); |
145 | exit(1); |
146 | } |
147 | if (memcmp(rx_buf, msg, msg_len) || |
148 | rx_buf[msg_len] != '\r' || rx_buf[msg_len+1] != '\n') { |
149 | fprintf(stderr, "echo mismatch\n"); |
150 | exit(1); |
151 | } |
152 | |
153 | if (memchr(rx_buf, 0, got)) { |
154 | fprintf(stderr, "NUL in response\n"); |
155 | exit(1); |
156 | } |
157 | |
158 | rx_buf += msg_len+2; |
159 | got -= msg_len+2; |
160 | |
161 | res = realloc(res, got+1); |
162 | if (!res) { |
163 | perror("realloc"); |
164 | exit(1); |
165 | } |
166 | |
167 | t = res; |
168 | for (s = rx_buf; s != rx_buf+got; s++) { |
169 | if (*s == '\r') { |
170 | if (s+1 != rx_buf+got && s[1] == '\n') |
171 | continue; |
172 | fprintf(stderr, "\\r without \\n\n"); |
173 | exit(1); |
174 | } |
175 | if (*s == '\n' && s+1 == rx_buf+got) |
176 | break; |
177 | *t++ = *s; |
178 | } |
179 | *t = 0; |
180 | |
181 | return res; |
182 | } |
183 | |
184 | |
185 | static const char *vdialog(int idle, const char *cmd, va_list ap) |
186 | { |
187 | char *msg; |
188 | |
189 | vasprintf(&msg, cmd, ap); |
190 | return dialog_fixed(idle, msg); |
191 | } |
192 | |
193 | |
194 | static const char *dialog(int idle, const char *cmd, ...) |
195 | { |
196 | va_list ap; |
197 | const char *res; |
198 | |
199 | va_start(ap, cmd); |
200 | res = vdialog(idle, cmd, ap); |
201 | va_end(ap); |
202 | return res; |
203 | } |
204 | |
205 | |
206 | static const char *dialog_rc(int idle, const char *cmd, ...) |
207 | { |
208 | va_list ap; |
209 | const char *res, *p; |
210 | unsigned rc; |
211 | |
212 | va_start(ap, cmd); |
213 | res = vdialog(idle, cmd, ap); |
214 | va_end(ap); |
215 | |
216 | p = strchr(res, '\n'); |
217 | if (!p) |
218 | p = strchr(res, 0); |
219 | if (sscanf(res, "%u", &rc) != 1) { |
220 | fprintf(stderr, "invalid status\n"); |
221 | exit(1); |
222 | } |
223 | |
224 | if (rc != 0) { |
225 | fprintf(stderr, "rc %d\n", rc); |
226 | exit(1); |
227 | } |
228 | |
229 | return p+1; |
230 | } |
231 | |
232 | |
233 | static int autobaud(void) |
234 | { |
235 | uint8_t reply[100]; |
236 | int i, got; |
237 | const char *res; |
238 | |
239 | for (i = 0; i != AUTOBAUD_TRIES; i++) { |
240 | CLR(pins[pin_nreset].pin); |
241 | usleep(10); /* DS Table 9 pg 29 says min 50 ns */ |
242 | SET(pins[pin_nreset].pin); |
243 | |
244 | usleep(5*1000); /* UM 26.3.1 pg 408 says max 3 ms */ |
245 | |
246 | got = swuart_trx("?", 1, reply, sizeof(reply), 1000, 100); |
247 | if (got != strlen(SYNC)+2 || memcmp(reply, SYNC "\r\n", got)) |
248 | continue; |
249 | |
250 | res = dialog(100, SYNC); |
251 | |
252 | if (!strcmp(res, "OK")) |
253 | return 1; |
254 | } |
255 | return 0; |
256 | } |
257 | |
258 | |
259 | /* ----- Devices database -------------------------------------------------- */ |
260 | |
261 | |
262 | static const struct device { |
263 | const char *name; |
264 | unsigned id; |
265 | int flash_kb; |
266 | } devices[] = { |
267 | { "LPC1112FHxxx/202", 0x2524902b, 16 }, |
268 | { NULL, } |
269 | }, *device = NULL; |
270 | |
271 | |
272 | static void identify(void) |
273 | { |
274 | const char *res; |
275 | unsigned id, serial[4]; |
276 | |
277 | res = dialog_rc(100, "J"); |
278 | if (sscanf(res, "%u", &id) != 1) { |
279 | fprintf(stderr, "J: cannot parse ID \"%s\"\n", res); |
280 | exit(1); |
281 | } |
282 | |
283 | res = dialog_rc(100, "N"); |
284 | if (sscanf(res, "%u %u %u %u", |
285 | serial, serial+1, serial+2, serial+3) != 4) { |
286 | fprintf(stderr, "N: cannot parse serial number\"%s\"\n", res); |
287 | exit(1); |
288 | } |
289 | |
290 | for (device = devices; device->name; device++) |
291 | if (device->id == id) { |
292 | if (!quiet) |
293 | fprintf(stderr, "%s (0x%04x %04x) %d kB " |
294 | "serial %08x.%08x.%08x.%08x\n", |
295 | device->name, id >> 16, id & 0xffff, |
296 | device->flash_kb, |
297 | serial[0], serial[1], serial[2], serial[3]); |
298 | return; |
299 | } |
300 | |
301 | fprintf(stderr, "unknown device 0x%04x %04x\n", id >> 16, id & 0xffff); |
302 | exit(1); |
303 | } |
304 | |
305 | |
306 | /* ----- Flash programming ------------------------------------------------- */ |
307 | |
308 | |
309 | static int ms_to_bits(int ms) |
310 | { |
311 | return (BPS*ms+999)/1000; |
312 | } |
313 | |
314 | |
315 | static char uuechar(uint8_t b) |
316 | { |
317 | b &= 0x3f; |
318 | return b ? b+32 : 0x60; |
319 | } |
320 | |
321 | |
322 | static const char *uuencode(const uint8_t *p, int len) |
323 | { |
324 | static char buf[80]; |
325 | char *t = buf; |
326 | const uint8_t *end = p+len; |
327 | unsigned tmp = 0; /* initialize to prevent compiler complaints */ |
328 | int i; |
329 | |
330 | *t++ = len+32; |
331 | while (p != end) { |
332 | for (i = 0; i != 3; i++) |
333 | if (p == end) |
334 | tmp <<= 8; |
335 | else |
336 | tmp = tmp << 8 | *p++; |
337 | *t++ = uuechar(tmp >> 18); |
338 | *t++ = uuechar(tmp >> 12); |
339 | *t++ = uuechar(tmp >> 6); |
340 | *t++ = uuechar(tmp); |
341 | } |
342 | *t = 0; |
343 | return buf; |
344 | } |
345 | |
346 | |
347 | static void flash_erase_sectors(unsigned addr, unsigned len) |
348 | { |
349 | unsigned from = addr/SECTOR; |
350 | unsigned to = (addr+len-1)/SECTOR; |
351 | |
352 | assert(!(addr & (SECTOR-1))); |
353 | assert(!(len & (SECTOR-1))); |
354 | |
355 | dialog_rc(100, "P %d %d", from, to); |
356 | |
357 | /* DS 10.2 pg 77: t_er(max) = 105 ms */ |
358 | dialog_rc(ms_to_bits(200), "E %d %d", from, to); |
359 | } |
360 | |
361 | |
362 | static void flash_write_page(unsigned addr, const uint8_t *buf) |
363 | { |
364 | unsigned sector = addr/SECTOR; |
365 | unsigned sum; |
366 | int chunk, i; |
367 | const char *res; |
368 | |
369 | assert(!(addr & (PAGE-1))); |
370 | |
371 | dialog_rc(100, "W %u %u", RAM_BUFFER, PAGE); |
372 | for (i = 0; i != PAGE; i += chunk) { |
373 | chunk = PAGE-i > MAX_RECORD ? MAX_RECORD : PAGE-i; |
374 | dialog(100, "%s", uuencode(buf+i, chunk)); |
375 | } |
376 | |
377 | sum = 0; |
378 | for (i = 0; i != PAGE; i++) |
379 | sum += buf[i]; |
380 | |
381 | res = dialog(100, "%u", sum); |
382 | if (strcmp(res, "OK")) { |
383 | fprintf(stderr, "non-OK response: \"%s\"\n", res); |
384 | exit(1); |
385 | } |
386 | |
387 | dialog_rc(100, "P %d %d", sector, sector); |
388 | |
389 | /* DS 10.2 pg 77: t_prog(max) = 1.05 ms */ |
390 | dialog_rc(ms_to_bits(2), "C %d %u %d", addr, RAM_BUFFER, PAGE); |
391 | } |
392 | |
393 | |
394 | static void flash_write(unsigned addr, const uint8_t *p, int len, |
395 | int erase_all) |
396 | { |
397 | uint8_t page[PAGE]; |
398 | |
399 | assert(!(addr & (PAGE-1))); |
400 | |
401 | dialog_rc(100, "U 23130"); |
402 | if (erase_all) |
403 | flash_erase_sectors(0, device->flash_kb << 10); |
404 | else |
405 | flash_erase_sectors(addr & ~(SECTOR-1), |
406 | (addr+len+SECTOR-1) & ~(SECTOR-1)); |
407 | while (len > 0) { |
408 | if (len < PAGE) { |
409 | memcpy(page, p, len); |
410 | memset(page+len, 0xff, PAGE-len); |
411 | } else { |
412 | memcpy(page, p, PAGE); |
413 | p += PAGE; |
414 | } |
415 | flash_write_page(addr, page); |
416 | addr += PAGE; |
417 | len -= PAGE; |
418 | } |
419 | } |
420 | |
421 | |
422 | static void flash_file(FILE *file) |
423 | { |
424 | uint8_t buf[(device->flash_kb << 10)+1]; |
425 | size_t got; |
426 | |
427 | got = fread(buf, 1, sizeof(buf), file); |
428 | if (!got) { |
429 | if (ferror(file)) |
430 | perror("fread"); |
431 | else |
432 | fprintf(stderr, "file is empty\n"); |
433 | exit(1); |
434 | } |
435 | if (got > device->flash_kb << 10) { |
436 | fprintf(stderr, "file is larger than flash (%d kB)\n", |
437 | device->flash_kb); |
438 | exit(1); |
439 | } |
440 | flash_write(0, buf, got, 1); |
441 | } |
442 | |
443 | |
444 | /* ----- Flash dump -------------------------------------------------------- */ |
445 | |
446 | |
447 | /* |
448 | * The uuencoding algorithm is described in AN11229. |
449 | * It's the same as used by uuencode. |
450 | */ |
451 | |
452 | |
453 | static uint8_t uudchar(char c) |
454 | { |
455 | if (c <= 0x20 && c > 0x60) { |
456 | fprintf(stderr, "invalid UU character '%c'\n", c); |
457 | exit(1); |
458 | } |
459 | return c == 0x60 ? 0 : c-32; |
460 | } |
461 | |
462 | |
463 | static int uudecode(const char *s, uint8_t *buf) |
464 | { |
465 | int len, uu_len; |
466 | const char *nl; |
467 | const uint8_t *end; |
468 | |
469 | len = *s-32; |
470 | if (len < 0 || len > MAX_RECORD) { |
471 | fprintf(stderr, "invalid UU length (%d)\n", len); |
472 | exit(1); |
473 | } |
474 | nl = strchr(++s, '\n'); |
475 | if (!nl) { |
476 | fprintf(stderr, "no \\n at end of UU record\n"); |
477 | exit(1); |
478 | } |
479 | uu_len = nl-s; |
480 | if (uu_len & 3) { |
481 | fprintf(stderr, "UU length %d not a multiple of 4\n", uu_len); |
482 | exit(1); |
483 | } |
484 | if ((len+2)/3 != uu_len/4) { |
485 | fprintf(stderr, "UU length %d vs. %d bytes (\"%.*s\")\n", |
486 | uu_len, len, nl-s+1, s-1); |
487 | exit(1); |
488 | } |
489 | |
490 | end = buf+len; |
491 | while (buf != end) { |
492 | unsigned tmp = uudchar(s[0]) << 18 | uudchar(s[1]) << 12 | |
493 | uudchar(s[2]) << 6 | uudchar(s[3]); |
494 | |
495 | *buf++ = tmp >> 16; |
496 | if (buf != end) |
497 | *buf++ = tmp >> 8; |
498 | if (buf != end) |
499 | *buf++ = tmp; |
500 | s += 4; |
501 | } |
502 | return len; |
503 | } |
504 | |
505 | |
506 | static void dump(void) |
507 | { |
508 | int addr = 0, end; |
509 | unsigned sum = 0, check; |
510 | const char *res, *nl; |
511 | int got, wrote, i; |
512 | |
513 | end = device->flash_kb << 10; |
514 | res = dialog_rc(100, "R 0 %u", end); |
515 | while (addr != end) { |
516 | while (1) { |
517 | uint8_t buf[MAX_RECORD]; |
518 | |
519 | nl = strchr(res, '\n'); |
520 | if (!nl) |
521 | break; |
522 | got = uudecode(res, buf); |
523 | for (i = 0; i != got; i++) |
524 | sum += buf[i]; |
525 | wrote = fwrite(buf, 1, got, stdout); |
526 | if (wrote != got) { |
527 | perror("fwrite"); |
528 | exit(1); |
529 | } |
530 | addr += got; |
531 | res = nl+1; |
532 | } |
533 | if (sscanf(res, "%u", &check) != 1) { |
534 | fprintf(stderr, "can't parse checksum \"%s\"\n", res); |
535 | exit(1); |
536 | } |
537 | if (check != sum) { |
538 | fprintf(stderr, "checksum error: got %u received %u\n", |
539 | sum, check); |
540 | exit(1); |
541 | } |
542 | res = dialog(100, "OK"); |
543 | sum = 0; |
544 | } |
545 | } |
546 | |
547 | |
548 | /* ----- ISP session ------------------------------------------------------- */ |
549 | |
550 | |
551 | static void at_exit(void) |
552 | { |
553 | ubb_close(0); |
554 | } |
555 | |
556 | |
557 | static void start_isp(int power) |
558 | { |
559 | const char *res; |
560 | |
561 | if (ubb_open(0) < 0) { |
562 | perror("ubb_open"); |
563 | exit(1); |
564 | } |
565 | atexit(at_exit); |
566 | |
567 | if (power) |
568 | ubb_power(1); |
569 | |
570 | usleep(100*1000); |
571 | |
572 | SET(pins[pin_nreset].pin); |
573 | OUT(pins[pin_nreset].pin); |
574 | |
575 | CLR(pins[pin_nisp].pin); |
576 | OUT(pins[pin_nisp].pin); |
577 | |
578 | if (swuart_open(pins[pin_rxd].pin, pins[pin_txd].pin, BPS) < 0) { |
579 | perror("swuart_open"); |
580 | exit(1); |
581 | } |
582 | |
583 | if (!autobaud()) { |
584 | fprintf(stderr, "target is not responding\n"); |
585 | exit(1); |
586 | } |
587 | |
588 | res = dialog(100, "12000"); |
589 | if (strcmp(res, "OK")) { |
590 | fprintf(stderr, "cannot set clock rate\n"); |
591 | exit(1); |
592 | } |
593 | } |
594 | |
595 | |
596 | /* ----- Reset and run target ---------------------------------------------- */ |
597 | |
598 | |
599 | static void run_target(int power) |
600 | { |
601 | if (ubb_open(0) < 0) { |
602 | perror("ubb_open"); |
603 | exit(1); |
604 | } |
605 | |
606 | if (power) |
607 | ubb_power(1); |
608 | |
609 | SET(pins[pin_nreset].pin); |
610 | OUT(pins[pin_nreset].pin); |
611 | |
612 | IN(pins[pin_nisp].pin); |
613 | |
614 | CLR(pins[pin_nreset].pin); |
615 | usleep(10); /* DS Table 9 pg 29 says min 50 ns */ |
616 | SET(pins[pin_nreset].pin); |
617 | |
618 | ubb_close(UBB_nPWR | pins[pin_nreset].pin | pins[pin_nisp].pin); |
619 | } |
620 | |
621 | |
622 | /* ----- Command-line processing ------------------------------------------- */ |
623 | |
624 | |
625 | static void usage(const char *name) |
626 | { |
627 | const struct signal *signal; |
628 | int i; |
629 | |
630 | fprintf(stderr, |
631 | "usage: %s [-P function=signal ...] [-n] [-q] [-v ...] [file.bin]\n" |
632 | " %s [-P function=signal ...] [-n] -r\n\n" |
633 | " -n don't power the device\n" |
634 | " -q suppress basic progress messages\n" |
635 | " -P function=signal assign a 8:10 interface signal to a function " |
636 | "(see below)\n" |
637 | " -r reset the target and let it run\n" |
638 | " -v increase verbosity level\n\n" |
639 | "Functions: RXD (P1_6), TXD (P1_7), nISP (P0_1), nRESET (P0_0)\n" |
640 | "Signals: CMD, CLK, DAT0, DAT1, DAT2, DAT3\n" |
641 | , name, name); |
642 | |
643 | fprintf(stderr, "Default mapping:"); |
644 | for (i = 0; i != pin_end; i++) { |
645 | for (signal = signals; signal->pin != pins[i].pin; signal++); |
646 | fprintf(stderr, " -P %s=%s", pins[i].name, signal->name); |
647 | } |
648 | fprintf(stderr, "\n"); |
649 | exit(1); |
650 | } |
651 | |
652 | |
653 | static void assign_pin(const char *name, const char *s) |
654 | { |
655 | const char *eq; |
656 | struct pin *pin; |
657 | const struct signal *signal; |
658 | |
659 | eq = strchr(s, '='); |
660 | if (!eq) |
661 | usage(name); |
662 | |
663 | for (pin = pins; pin->name; pin++) { |
664 | if (strlen(pin->name) == eq-s && |
665 | !strncasecmp(pin->name, s, eq-s)) |
666 | break; |
667 | if (strlen(pin->alt_name) == eq-s && |
668 | !strncasecmp(pin->alt_name, s, eq-s)) |
669 | break; |
670 | } |
671 | if (!pin->name) { |
672 | fprintf(stderr, "unknown function \"%.*s\"\n", eq-s, s); |
673 | exit(1); |
674 | } |
675 | |
676 | for (signal = signals; signal->name; signal++) |
677 | if (!strcasecmp(signal->name, eq+1)) |
678 | break; |
679 | if (!signal->name) { |
680 | fprintf(stderr, "unknown signal \"%s\"\n", eq+1); |
681 | exit(1); |
682 | } |
683 | |
684 | pin->pin = signal->pin; |
685 | } |
686 | |
687 | |
688 | int main(int argc, char **argv) |
689 | { |
690 | FILE *file = NULL; |
691 | int power = 1; |
692 | int run = 0; |
693 | int c; |
694 | |
695 | while ((c = getopt(argc, argv, "nP:qrv")) != EOF) |
696 | switch (c) { |
697 | case 'n': |
698 | power = 0; |
699 | break; |
700 | case 'P': |
701 | assign_pin(*argv, optarg); |
702 | break; |
703 | case 'q': |
704 | quiet = 1; |
705 | break; |
706 | case 'r': |
707 | run = 1; |
708 | break; |
709 | case 'v': |
710 | verbose++; |
711 | break; |
712 | default: |
713 | usage(*argv); |
714 | } |
715 | |
716 | if (run) { |
717 | if (quiet || verbose || argc != optind) |
718 | usage(*argv); |
719 | run_target(power); |
720 | return 0; |
721 | } |
722 | |
723 | switch (argc-optind) { |
724 | case 0: |
725 | break; |
726 | case 1: |
727 | if (!strcmp(argv[optind], "-")) |
728 | file = stdin; |
729 | else { |
730 | file = fopen(argv[optind], "r"); |
731 | if (!file) { |
732 | perror(argv[optind]); |
733 | exit(1); |
734 | } |
735 | } |
736 | break; |
737 | default: |
738 | usage(*argv); |
739 | } |
740 | |
741 | start_isp(power); |
742 | |
743 | identify(); |
744 | |
745 | if (file) |
746 | flash_file(file); |
747 | else |
748 | dump(); |
749 | |
750 | return 0; |
751 | } |
752 |
Branches:
master