/* * avrdude - A Downloader/Uploader for AVR device programmers * Copyright (C) 2000, 2001, 2002, 2003 Brian S. Dean * * 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. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* $Id: fileio.c,v 1.21 2003/02/14 20:34:02 troth Exp $ */ /* #include "ac_cfg.h" */ #include #include #include #include #include #include #include "avr.h" #include "fileio.h" #define IHEX_MAXDATA 256 #define MAX_LINE_LEN 256 /* max line length for ASCII format input files */ struct ihexrec { unsigned char reclen; unsigned int loadofs; unsigned char rectyp; unsigned char data[IHEX_MAXDATA]; unsigned char cksum; }; extern char * progname; extern char progbuf[]; int b2ihex(unsigned char * inbuf, int bufsize, int recsize, int startaddr, char * outfile, FILE * outf); int ihex2b(char * infile, FILE * inf, unsigned char * outbuf, int bufsize); int b2srec(unsigned char * inbuf, int bufsize, int recsize, int startaddr, char * outfile, FILE * outf); int srec2b(char * infile, FILE * inf, unsigned char * outbuf, int bufsize); int ihex_readrec(struct ihexrec * ihex, char * rec); int srec_readrec(struct ihexrec * srec, char * rec); int fileio_rbin(struct fioparms * fio, char * filename, FILE * f, unsigned char * buf, int size); int fileio_ihex(struct fioparms * fio, char * filename, FILE * f, unsigned char * buf, int size); int fileio_srec(struct fioparms * fio, char * filename, FILE * f, unsigned char * buf, int size); int fmt_autodetect(char * fname); char * fmtstr(FILEFMT format) { switch (format) { case FMT_AUTO : return "auto-detect"; break; case FMT_SREC : return "Motorola S-Record"; break; case FMT_IHEX : return "Intel Hex"; break; case FMT_RBIN : return "raw binary"; break; default : return "invalid format"; break; }; } int b2ihex(unsigned char * inbuf, int bufsize, int recsize, int startaddr, char * outfile, FILE * outf) { unsigned char * buf; unsigned int nextaddr; int n, nbytes, n_64k; int i; unsigned char cksum; if (recsize > 255) { fprintf(stderr, "%s: recsize=%d, must be < 256\n", progname, recsize); return -1; } n_64k = 0; nextaddr = startaddr; buf = inbuf; nbytes = 0; while (bufsize) { n = recsize; if (n > bufsize) n = bufsize; if ((nextaddr + n) > 0x10000) n = 0x10000 - nextaddr; if (n) { cksum = 0; fprintf(outf, ":%02X%04X00", n, nextaddr); cksum += n + ((nextaddr >> 8) & 0x0ff) + (nextaddr & 0x0ff); for (i=0; i= 0x10000) { int lo, hi; /* output an extended address record */ n_64k++; lo = n_64k & 0xff; hi = (n_64k >> 8) & 0xff; cksum = 0; fprintf(outf, ":02000004%02X%02X", hi, lo); cksum += 2 + 0 + 4 + hi + lo; cksum = -cksum; fprintf(outf, "%02X\n", cksum); nextaddr = 0; } /* advance to next 'recsize' bytes */ buf += n; bufsize -= n; } /*----------------------------------------------------------------- add the end of record data line -----------------------------------------------------------------*/ cksum = 0; n = 0; nextaddr = 0; fprintf(outf, ":%02X%04X01", n, nextaddr); cksum += n + ((nextaddr >> 8) & 0x0ff) + (nextaddr & 0x0ff) + 1; cksum = -cksum; fprintf(outf, "%02X\n", cksum); return nbytes; } int ihex_readrec(struct ihexrec * ihex, char * rec) { int i, j; char buf[8]; int offset, len; char * e; unsigned char cksum; int rc; len = strlen(rec); offset = 1; cksum = 0; /* reclen */ if (offset + 2 > len) return -1; for (i=0; i<2; i++) buf[i] = rec[offset++]; buf[i] = 0; ihex->reclen = strtoul(buf, &e, 16); if (e == buf || *e != 0) return -1; /* load offset */ if (offset + 4 > len) return -1; for (i=0; i<4; i++) buf[i] = rec[offset++]; buf[i] = 0; ihex->loadofs = strtoul(buf, &e, 16); if (e == buf || *e != 0) return -1; /* record type */ if (offset + 2 > len) return -1; for (i=0; i<2; i++) buf[i] = rec[offset++]; buf[i] = 0; ihex->rectyp = strtoul(buf, &e, 16); if (e == buf || *e != 0) return -1; cksum = ihex->reclen + ((ihex->loadofs >> 8) & 0x0ff) + (ihex->loadofs & 0x0ff) + ihex->rectyp; /* data */ for (j=0; jreclen; j++) { if (offset + 2 > len) return -1; for (i=0; i<2; i++) buf[i] = rec[offset++]; buf[i] = 0; ihex->data[j] = strtoul(buf, &e, 16); if (e == buf || *e != 0) return -1; cksum += ihex->data[j]; } /* cksum */ if (offset + 2 > len) return -1; for (i=0; i<2; i++) buf[i] = rec[offset++]; buf[i] = 0; ihex->cksum = strtoul(buf, &e, 16); if (e == buf || *e != 0) return -1; rc = -cksum & 0x000000ff; return rc; } /* * Intel Hex to binary buffer * * Given an open file 'inf' which contains Intel Hex formated data, * parse the file and lay it out within the memory buffer pointed to * by outbuf. The size of outbuf, 'bufsize' is honored; if data would * fall outsize of the memory buffer outbuf, an error is generated. * * Return the maximum memory address within 'outbuf' that was written. * If an error occurs, return -1. * * */ int ihex2b(char * infile, FILE * inf, unsigned char * outbuf, int bufsize) { char buffer [ MAX_LINE_LEN ]; unsigned char * buf; unsigned int nextaddr, baseaddr, maxaddr; int i; int lineno; int len; struct ihexrec ihex; int rc; lineno = 0; buf = outbuf; baseaddr = 0; maxaddr = 0; while (fgets((char *)buffer,MAX_LINE_LEN,inf)!=NULL) { lineno++; len = strlen(buffer); if (buffer[len-1] == '\n') buffer[--len] = 0; if (buffer[0] != ':') continue; rc = ihex_readrec(&ihex, buffer); if (rc < 0) { fprintf(stderr, "%s: invalid record at line %d of \"%s\"\n", progname, lineno, infile); return -1; } else if (rc != ihex.cksum) { fprintf(stderr, "%s: ERROR: checksum mismatch at line %d of \"%s\"\n", progname, lineno, infile); fprintf(stderr, "%s: checksum=0x%02x, computed checksum=0x%02x\n", progname, ihex.cksum, rc); return -1; } switch (ihex.rectyp) { case 0: /* data record */ nextaddr = ihex.loadofs + baseaddr; if (nextaddr + ihex.reclen > bufsize) { fprintf(stderr, "%s: ERROR: address 0x%04x out of range at line %d of %s\n", progname, nextaddr+ihex.reclen, lineno, infile); return -1; } for (i=0; i maxaddr) maxaddr = nextaddr+ihex.reclen; break; case 1: /* end of file record */ return maxaddr; break; case 2: /* extended segment address record */ baseaddr = (ihex.data[0] << 8 | ihex.data[1]) << 4; break; case 3: /* start segment address record */ /* we don't do anything with the start address */ break; case 4: /* extended linear address record */ baseaddr = (ihex.data[0] << 8 | ihex.data[1]) << 16; break; case 5: /* start linear address record */ /* we don't do anything with the start address */ break; default: fprintf(stderr, "%s: don't know how to deal with rectype=%d " "at line %d of %s\n", progname, ihex.rectyp, lineno, infile); return -1; break; } } /* while */ fprintf(stderr, "%s: WARNING: no end of file record found for Intel Hex " "file \"%s\"\n", progname, infile); return maxaddr; } int b2srec(unsigned char * inbuf, int bufsize, int recsize, int startaddr, char * outfile, FILE * outf) { unsigned char * buf; unsigned int nextaddr, stopaddr; int n, nbytes, addr_width; int i; unsigned char cksum; unsigned char startf, stopf ,emptyf; char * tmpl=0; if (recsize > 255) { fprintf(stderr, "%s: ERROR: recsize=%d, must be < 256\n", progname, recsize); return -1; } buf = inbuf; nextaddr = 0; stopaddr = 0; startf = 0; stopf = 0; /* search for ranges of 'real' data */ for (i=startaddr; i bufsize) n = bufsize; if (n) { cksum = 0; if (nextaddr + n <= 0xffff) { addr_width = 2; tmpl="S1%02X%04X"; } else if (nextaddr + n <= 0xffffff) { addr_width = 3; tmpl="S2%02X%06X"; } else if (nextaddr + n <= 0xffffffff) { addr_width = 4; tmpl="S3%02X%08X"; } else { fprintf(stderr, "%s: ERROR: address=%d, out of range\n", progname, nextaddr); return -1; } /* skip the lines filled with 0xff */ emptyf = 1; for (i=nextaddr; i0; i--) cksum += (nextaddr >> (i-1) * 8) & 0xff; for (i=nextaddr; i0; i--) cksum += (nextaddr >> (i - 1) * 8) & 0xff; cksum = 0xff - cksum; fprintf(outf, "%02X\n", cksum); return nbytes; } int srec_readrec(struct ihexrec * srec, char * rec) { int i, j; char buf[8]; int offset, len, addr_width; char * e; unsigned char cksum; int rc; len = strlen(rec); offset = 1; cksum = 0; addr_width = 2; /* record type */ if (offset + 1 > len) return -1; srec->rectyp = rec[offset++]; if (srec->rectyp == 0x32 || srec->rectyp == 0x38) addr_width = 3; /* S2,S8-record */ else if (srec->rectyp == 0x33 || srec->rectyp == 0x37) addr_width = 4; /* S3,S7-record */ /* reclen */ if (offset + 2 > len) return -1; for (i=0; i<2; i++) buf[i] = rec[offset++]; buf[i] = 0; srec->reclen = strtoul(buf, &e, 16); cksum += srec->reclen; srec->reclen -= (addr_width+1); if (e == buf || *e != 0) return -1; /* load offset */ if (offset + addr_width > len) return -1; for (i=0; iloadofs = strtoull(buf, &e, 16); if (e == buf || *e != 0) return -1; for (i=addr_width; i>0; i--) cksum += (srec->loadofs >> (i - 1) * 8) & 0xff; /* data */ for (j=0; jreclen; j++) { if (offset+2 > len) return -1; for (i=0; i<2; i++) buf[i] = rec[offset++]; buf[i] = 0; srec->data[j] = strtoul(buf, &e, 16); if (e == buf || *e != 0) return -1; cksum += srec->data[j]; } /* cksum */ if (offset + 2 > len) return -1; for (i=0; i<2; i++) buf[i] = rec[offset++]; buf[i] = 0; srec->cksum = strtoul(buf, &e, 16); if (e == buf || *e != 0) return -1; rc = 0xff - cksum; return rc; } int srec2b(char * infile, FILE * inf, unsigned char * outbuf, int bufsize) { char buffer [ MAX_LINE_LEN ]; unsigned char * buf; unsigned int nextaddr, baseaddr, maxaddr; int i; int lineno; int len; struct ihexrec srec; int rc; int reccount; unsigned char datarec; char * msg = 0; lineno = 0; buf = outbuf; baseaddr = 0; maxaddr = 0; reccount = 0; while (fgets((char *)buffer,MAX_LINE_LEN,inf)!=NULL) { lineno++; len = strlen(buffer); if (buffer[len-1] == '\n') buffer[--len] = 0; if (buffer[0] != 0x53) continue; rc = srec_readrec(&srec, buffer); if (rc < 0) { fprintf(stderr, "%s: ERROR: invalid record at line %d of \"%s\"\n", progname, lineno, infile); return -1; } else if (rc != srec.cksum) { fprintf(stderr, "%s: ERROR: checksum mismatch at line %d of \"%s\"\n", progname, lineno, infile); fprintf(stderr, "%s: checksum=0x%02x, computed checksum=0x%02x\n", progname, srec.cksum, rc); return -1; } datarec=0; switch (srec.rectyp) { case 0x30: /* S0 - header record*/ /* skip */ break; case 0x31: /* S1 - 16 bit address data record */ datarec=1; msg="%s: ERROR: address 0x%04x out of range at line %d of %s\n"; break; case 0x32: /* S2 - 24 bit address data record */ datarec=1; msg="%s: ERROR: address 0x%06x out of range at line %d of %s\n"; break; case 0x33: /* S3 - 32 bit address data record */ datarec=1; msg="%s: ERROR: address 0x%08x out of range at line %d of %s\n"; break; case 0x34: /* S4 - symbol record (LSI extension) */ fprintf(stderr, "%s: ERROR: not supported record at line %d of %s\n", progname, lineno, infile); return -1; case 0x35: /* S5 - count of S1,S2 and S3 records previously transmitted */ if (srec.loadofs != reccount){ fprintf(stderr, "%s: ERROR: count of transmitted data records mismatch at line %d of \"%s\"\n", progname, lineno, infile); fprintf(stderr, "%s: transmitted data records= %d, expected value= %d\n", progname, reccount, srec.loadofs); return -1; } break; case 0x37: /* S7 Record - end record for 32 bit address data */ case 0x38: /* S8 Record - end record for 24 bit address data */ case 0x39: /* S9 Record - end record for 16 bit address data */ return maxaddr; default: fprintf(stderr, "%s: ERROR: don't know how to deal with rectype S%d " "at line %d of %s\n", progname, srec.rectyp, lineno, infile); return -1; } if (datarec == 1) { nextaddr = srec.loadofs + baseaddr; if (nextaddr + srec.reclen > bufsize) { fprintf(stderr, msg, progname, nextaddr+srec.reclen, lineno, infile); return -1; } for (i=0; i maxaddr) maxaddr = nextaddr+srec.reclen; reccount++; } } fprintf(stderr, "%s: WARNING: no end of file record found for Motorola S-Records " "file \"%s\"\n", progname, infile); return maxaddr; } int fileio_rbin(struct fioparms * fio, char * filename, FILE * f, unsigned char * buf, int size) { int rc; switch (fio->op) { case FIO_READ: rc = fread(buf, 1, size, f); break; case FIO_WRITE: rc = fwrite(buf, 1, size, f); break; default: fprintf(stderr, "%s: fileio: invalid operation=%d\n", progname, fio->op); return -1; } if (rc < 0 || (fio->op == FIO_WRITE && rc < size)) { fprintf(stderr, "%s: %s error %s %s: %s; %s %d of the expected %d bytes\n", progname, fio->iodesc, fio->dir, filename, strerror(errno), fio->rw, rc, size); return -1; } return rc; } int fileio_ihex(struct fioparms * fio, char * filename, FILE * f, unsigned char * buf, int size) { int rc; switch (fio->op) { case FIO_WRITE: rc = b2ihex(buf, size, 32, 0, filename, f); if (rc < 0) { return -1; } break; case FIO_READ: rc = ihex2b(filename, f, buf, size); if (rc < 0) return -1; break; default: fprintf(stderr, "%s: invalid Intex Hex file I/O operation=%d\n", progname, fio->op); return -1; break; } return rc; } int fileio_srec(struct fioparms * fio, char * filename, FILE * f, unsigned char * buf, int size) { int rc; switch (fio->op) { case FIO_WRITE: rc = b2srec(buf, size, 32, 0, filename, f); if (rc < 0) { return -1; } break; case FIO_READ: rc = srec2b(filename, f, buf, size); if (rc < 0) return -1; break; default: fprintf(stderr, "%s: ERROR: invalid Motorola S-Records file I/O operation=%d\n", progname, fio->op); return -1; break; } return rc; } int fileio_setparms(int op, struct fioparms * fp) { fp->op = op; switch (op) { case FIO_READ: fp->mode = "r"; fp->iodesc = "input"; fp->dir = "from"; fp->rw = "read"; break; case FIO_WRITE: fp->mode = "w"; fp->iodesc = "output"; fp->dir = "to"; fp->rw = "wrote"; break; default: fprintf(stderr, "%s: invalid I/O operation %d\n", progname, op); return -1; break; } return 0; } int fmt_autodetect(char * fname) { FILE * f; unsigned char buf[MAX_LINE_LEN]; int i; int len; int found; f = fopen(fname, "r"); if (f == NULL) { fprintf(stderr, "%s: error opening %s: %s\n", progname, fname, strerror(errno)); return -1; } while (fgets((char *)buf, MAX_LINE_LEN, f)!=NULL) { buf[MAX_LINE_LEN-1] = 0; len = strlen((char *)buf); if (buf[len-1] == '\n') buf[--len] = 0; /* check for binary data */ found = 0; for (i=0; i 127) { found = 1; break; } } if (found) return FMT_RBIN; /* check for lines that look like intel hex */ if ((buf[0] == ':') && (len >= 11)) { found = 1; for (i=1; i= 10) && isdigit(buf[1])) { found = 1; for (i=1; idesc); return -1; } rc = fileio_setparms(op, &fio); if (rc < 0) return -1; if (strcmp(filename, "-")==0) { if (fio.op == FIO_READ) { fname = ""; f = stdin; } else { fname = ""; f = stdout; } } else { fname = filename; f = fopen(fname, fio.mode); if (f == NULL) { fprintf(stderr, "%s: can't open %s file %s: %s\n", progname, fio.iodesc, fname, strerror(errno)); return -1; } } /* point at the requested memory buffer */ buf = mem->buf; if (fio.op == FIO_READ) size = mem->size; if (fio.op == FIO_READ) { /* 0xff fill unspecified memory */ for (i=0; i