/* * lpc111x-isp/lpc111x.c - LPC111x/LPC11Cxx ISP programmer * * Written 2012 by Werner Almesberger * Copyright 2012 Werner Almesberger * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. */ #define _GNU_SOURCE /* for vasprintf */ #include #include #include #include #include #include #include #include #define TGT_nRESET UBB_CMD #define TGT_nISP UBB_DAT1 #define TGT_TX UBB_DAT3 #define TGT_RX UBB_DAT2 #define HOST_RX TGT_TX #define HOST_TX TGT_RX #define MAX_BUF 10000 #define AUTOBAUD_TRIES 10 #define SYNC "Synchronized" static int verbose = 0; static int quiet = 0; /* ----- Debugging and tracing --------------------------------------------- */ static void trace(const char *label, const uint8_t *s, int len) { const uint8_t *end = s+len; if (label) printf("%s ", label); for (end = s+len; s != end; s++) { if (*s >= ' ' && *s <= '~') printf("%c", *s); else if (*s == 10) printf("\\n"); else if (*s == 13) printf("\\r"); else printf("\\%02o", *s); } printf("\n"); } static void trace_out(const void *s, int len) { trace(">>>", s, len); } static void trace_in(const void *s, int len) { trace("<<<", s, len); } /* ----- Dialog functions -------------------------------------------------- */ static const char *dialog_fixed(int idle, const char *msg) { static char *res = NULL; int msg_len = strlen(msg); char *tx_buf = alloca(msg_len+3); char *rx_buf = alloca(MAX_BUF); char *s, *t; int got; memcpy(tx_buf, msg, msg_len); memcpy(tx_buf+msg_len, "\r\n", 2); if (verbose) trace_out(tx_buf, msg_len+2); got = swuart_trx(tx_buf, msg_len+2, rx_buf, MAX_BUF, idle, idle); if (verbose) trace_in(rx_buf, got); if (got < msg_len+2) { fprintf(stderr, "response too short for echo\n"); exit(1); } if (memcmp(rx_buf, msg, msg_len) || rx_buf[msg_len] != '\r' || rx_buf[msg_len+1] != '\n') { fprintf(stderr, "echo mismatch\n"); exit(1); } if (memchr(rx_buf, 0, got)) { fprintf(stderr, "NUL in response\n"); exit(1); } rx_buf += msg_len+2; got -= msg_len+2; res = realloc(res, got+1); if (!res) { perror("realloc"); exit(1); } t = res; for (s = rx_buf; s != rx_buf+got; s++) { if (*s == '\r') { if (s+1 != rx_buf+got && s[1] == '\n') continue; fprintf(stderr, "\\r without \\n\n"); exit(1); } if (*s == '\n' && !s[1]) break; *t++ = *s; } *t = 0; return res; } static const char *vdialog(int idle, const char *cmd, va_list ap) { char *msg; vasprintf(&msg, cmd, ap); return dialog_fixed(idle, msg); } static const char *dialog(int idle, const char *cmd, ...) { va_list ap; const char *res; va_start(ap, cmd); res = vdialog(idle, cmd, ap); va_end(ap); return res; } static const char *dialog_rc(int idle, const char *cmd, ...) { va_list ap; const char *res, *p; unsigned rc; va_start(ap, cmd); res = vdialog(idle, cmd, ap); va_end(ap); p = strchr(res, '\n'); if (!p || sscanf(res, "%u", &rc) != 1) { fprintf(stderr, "invalid status\n"); exit(1); } if (rc != 0) { fprintf(stderr, "rc %d\n", rc); exit(1); } return p+1; } static int autobaud(void) { uint8_t reply[100]; int i, got; const char *res; for (i = 0; i != AUTOBAUD_TRIES; i++) { CLR(TGT_nRESET); usleep(10); /* DS Table 9 pg 29 says min 50 ns */ SET(TGT_nRESET); usleep(5*1000); /* UM 26.3.1 pg 408 says max 3 ms */ got = swuart_trx("?", 1, reply, sizeof(reply), 1000, 100); if (got != strlen(SYNC)+2 || memcmp(reply, SYNC "\r\n", got)) continue; res = dialog(100, SYNC); if (!strcmp(res, "OK")) return 1; } return 0; } /* ----- Devices database -------------------------------------------------- */ static const struct device { const char *name; unsigned id; int flash_kb; } devices[] = { { "LPC1112FHxxx/202", 0x2524902b, 16 }, { NULL, } }, *device = NULL; static void identify(void) { const char *res; unsigned id; res = dialog_rc(100, "J"); if (sscanf(res, "%u", &id) != 1) { fprintf(stderr, "J: cannot parse ID \"%s\"\n", res); exit(1); } for (device = devices; device->name; device++) if (device->id == id) { if (!quiet) fprintf(stderr, "%s (0x%04x %04x) %d kB\n", device->name, id >> 16, id & 0xffff, device->flash_kb); return; } fprintf(stderr, "unknown device 0x%04x %04x\n", id >> 16, id & 0xffff); exit(1); } /* ----- ISP session ------------------------------------------------------- */ static void at_exit(void) { ubb_close(0); } static void start_isp(void) { const char *res; if (ubb_open(0) < 0) { perror("ubb_open"); exit(1); } atexit(at_exit); ubb_power(1); usleep(100*1000); SET(TGT_nRESET); OUT(TGT_nRESET); CLR(TGT_nISP); OUT(TGT_nISP); swuart_open(HOST_TX, HOST_RX, 115200); if (!autobaud()) { fprintf(stderr, "target is not responding\n"); exit(1); } res = dialog(100, "12000"); if (strcmp(res, "OK")) { fprintf(stderr, "cannot set clock rate\n"); exit(1); } } /* ----- Command-line processing ------------------------------------------- */ static void usage(const char *name) { fprintf(stderr, "usage: %s [-q] [-v ...]\n\n" " -q suppress basic progress messages\n" " -v increase verbosity level\n" , name); exit(1); } int main(int argc, char **argv) { int c; while ((c = getopt(argc, argv, "qv")) != EOF) switch (c) { case 'q': quiet = 1; break; case 'v': verbose++; break; default: usage(*argv); } switch (argc-optind) { case 0: break; default: usage(*argv); } start_isp(); identify(); return 0; }