diff --git a/src/avr.c b/src/avr.c index 7f91e9a1..eea0ad8a 100644 --- a/src/avr.c +++ b/src/avr.c @@ -1220,7 +1220,55 @@ int avr_put_cycle_count(PROGRAMMER * pgm, AVRPART * p, int cycles) } return 0; +} + + +// Typical order in which memories show in avrdude.conf, runtime adds unknown ones (if any) +const char *avr_mem_order[100] = { + "eeprom", "flash", "application", "apptable", + "boot", "lfuse", "hfuse", "efuse", + "fuse", "fuse0", "wdtcfg", "fuse1", + "bodcfg", "fuse2", "osccfg", "fuse3", + "fuse4", "tcd0cfg", "fuse5", "syscfg0", + "fuse6", "syscfg1", "fuse7", "append", + "codesize", "fuse8", "fuse9", "bootend", + "bootsize", "fuses", "lock", "lockbits", + "tempsense", "signature", "prodsig", "sernum", + "calibration", "osccal16", "osccal20", "osc16err", + "osc20err", "usersig", "userrow", "data", +}; + +void avr_add_mem_order(const char *str) { + for(size_t i=0; i < sizeof avr_mem_order/sizeof *avr_mem_order; i++) { + if(avr_mem_order[i] && !strcmp(avr_mem_order[i], str)) + return; + if(!avr_mem_order[i]) { + avr_mem_order[i] = strdup(str); + return; + } } + avrdude_message(MSG_INFO, + "%s: avr_mem_order[] under-dimensioned in avr.c; increase and recompile\n", + progname); + exit(1); +} + +int avr_mem_is_known(const char *str) { + if(str && *str) + for(size_t i=0; i < sizeof avr_mem_order/sizeof *avr_mem_order; i++) + if(avr_mem_order[i] && !strcmp(avr_mem_order[i], str)) + return 1; + return 0; +} + +int avr_mem_might_be_known(const char *str) { + if(str && *str) + for(size_t i=0; i < sizeof avr_mem_order/sizeof *avr_mem_order; i++) + if(avr_mem_order[i] && !strncmp(avr_mem_order[i], str, strlen(str))) + return 1; + return 0; +} + int avr_chip_erase(PROGRAMMER * pgm, AVRPART * p) { diff --git a/src/config_gram.y b/src/config_gram.y index 68718dd1..ce11b924 100644 --- a/src/config_gram.y +++ b/src/config_gram.y @@ -1320,6 +1320,7 @@ part_parm : mem->desc[AVR_MEMDESCLEN-1] = 0; ladd(current_part->mem, mem); } + avr_add_mem_order($2->value.string); current_mem = mem; free_token($2); } diff --git a/src/developer_opts.c b/src/developer_opts.c index f4a55478..b82c1805 100644 --- a/src/developer_opts.c +++ b/src/developer_opts.c @@ -280,35 +280,6 @@ static void dev_stack_out(bool tsv, AVRPART *p, const char *name, unsigned char } -// order in which memories are processed, runtime adds unknown ones (but there shouldn't be any) -static const char *mem_order[100] = { - "eeprom", "flash", "application", "apptable", - "boot", "lfuse", "hfuse", "efuse", - "fuse", "fuse0", "wdtcfg", "fuse1", - "bodcfg", "fuse2", "osccfg", "fuse3", - "fuse4", "tcd0cfg", "fuse5", "syscfg0", - "fuse6", "syscfg1", "fuse7", "append", - "codesize", "fuse8", "fuse9", "bootend", - "bootsize", "fuses", "lock", "lockbits", - "tempsense", "signature", "prodsig", "sernum", - "calibration", "osccal16", "osccal20", "osc16err", - "osc20err", "usersig", "userrow", "data", -}; - -static void add_mem_order(const char *str) { - for(size_t i=0; i < sizeof mem_order/sizeof *mem_order; i++) { - if(mem_order[i] && !strcmp(mem_order[i], str)) - return; - if(!mem_order[i]) { - mem_order[i] = strdup(str); - return; - } - } - dev_info("%s: mem_order[] under-dimensioned in developer_opts.c; increase and recompile\n", progname); - exit(1); -} - - static int intcmp(int a, int b) { return a-b; } @@ -410,8 +381,8 @@ static int avrpart_deep_copy(AVRPARTdeep *d, AVRPART *p) { // fill in all memories we got in defined order di = 0; - for(size_t mi=0; mi < sizeof mem_order/sizeof *mem_order && mem_order[mi]; mi++) { - m = p->mem? avr_locate_mem(p, mem_order[mi]): NULL; + for(size_t mi=0; mi < sizeof avr_mem_order/sizeof *avr_mem_order && avr_mem_order[mi]; mi++) { + m = p->mem? avr_locate_mem(p, avr_mem_order[mi]): NULL; if(m) { if(di >= sizeof d->mems/sizeof *d->mems) { avrdude_message(MSG_INFO, "%s: ran out of mems[] space, increase size in AVRMEMdeep of developer_opts.c and recompile\n", progname); @@ -566,11 +537,11 @@ static void dev_part_strct(AVRPART *p, bool tsv, AVRPART *base) { if(!base || opcodecmp(p->op[i], base->op[i], i)) dev_part_strct_entry(tsv, ".ptop", p->desc, "part", opcodename(i), opcode2str(p->op[i], i, !tsv)); - for(size_t mi=0; mi < sizeof mem_order/sizeof *mem_order && mem_order[mi]; mi++) { + for(size_t mi=0; mi < sizeof avr_mem_order/sizeof *avr_mem_order && avr_mem_order[mi]; mi++) { AVRMEM *m, *bm; - m = p->mem? avr_locate_mem(p, mem_order[mi]): NULL; - bm = base && base->mem? avr_locate_mem(base, mem_order[mi]): NULL; + m = p->mem? avr_locate_mem(p, avr_mem_order[mi]): NULL; + bm = base && base->mem? avr_locate_mem(base, avr_mem_order[mi]): NULL; if(!m && bm && !tsv) dev_info("\n memory \"%s\" = NULL;\n", bm->desc); @@ -694,12 +665,12 @@ void dev_output_part_defs(char *partdesc) { AVRPART *p = ldata(ln1); if(p->mem) for(LNODEID lnm=lfirst(p->mem); lnm; lnm=lnext(lnm)) - add_mem_order(((AVRMEM *) ldata(lnm))->desc); + avr_add_mem_order(((AVRMEM *) ldata(lnm))->desc); // same for aliased memories (though probably not needed) if(p->mem_alias) for(LNODEID lnm=lfirst(p->mem_alias); lnm; lnm=lnext(lnm)) - add_mem_order(((AVRMEM_ALIAS *) ldata(lnm))->desc); + avr_add_mem_order(((AVRMEM_ALIAS *) ldata(lnm))->desc); } nprinted = dev_nprinted; diff --git a/src/libavrdude.h b/src/libavrdude.h index dad47922..a30c1402 100644 --- a/src/libavrdude.h +++ b/src/libavrdude.h @@ -783,6 +783,7 @@ void sort_programmers(LISTID programmers); typedef void (*FP_UpdateProgress)(int percent, double etime, char *hdr); extern struct avrpart parts[]; +extern const char *avr_mem_order[100]; extern FP_UpdateProgress update_progress; @@ -818,6 +819,11 @@ int avr_get_cycle_count(PROGRAMMER * pgm, AVRPART * p, int * cycles); int avr_put_cycle_count(PROGRAMMER * pgm, AVRPART * p, int cycles); +void avr_add_mem_order(const char *str); + +int avr_mem_is_known(const char *str); +int avr_mem_might_be_known(const char *str); + #define disable_trailing_ff_removal() avr_mem_hiaddr(NULL) int avr_mem_hiaddr(AVRMEM * mem); @@ -888,6 +894,7 @@ enum updateflags { UF_NONE = 0, UF_NOWRITE = 1, UF_AUTO_ERASE = 2, + UF_VERIFY = 4, }; @@ -898,6 +905,17 @@ typedef struct update_t { int format; } UPDATE; +typedef struct { // File reads for flash can exclude trailing 0xff, which are cut off + int nbytes, // Number of bytes set including 0xff but excluding cut off, trailing 0xff + nsections, // Number of consecutive sections in source excluding cut off, trailing 0xff + npages, // Number of memory pages needed excluding pages solely with trailing 0xff + nfill, // Number of fill bytes to make up full pages that are needed + ntrailing, // Number of trailing 0xff in source + firstaddr, // First address set in [0, mem->size-1] + lastaddr; // Highest address set by input file +} Filestats; + + #ifdef __cplusplus extern "C" { #endif @@ -910,6 +928,14 @@ extern void free_update(UPDATE * upd); extern int do_op(PROGRAMMER * pgm, struct avrpart * p, UPDATE * upd, enum updateflags flags); +extern int memstats(struct avrpart *p, char *memtype, int size, Filestats *fsp); + +// Convenience functions for printing +const char *plural(int x); +const char *inname(const char *fn); +const char *outname(const char *fn); +const char *interval(int a, int b); + #ifdef __cplusplus } #endif diff --git a/src/main.c b/src/main.c index 772762f3..9f72847f 100644 --- a/src/main.c +++ b/src/main.c @@ -237,7 +237,7 @@ static void cleanup_main(void) static void replace_backslashes(char *s) { // Replace all backslashes with forward slashes - for (int i = 0; i < strlen(s); i++) { + for (size_t i = 0; i < strlen(s); i++) { if (s[i] == '\\') { s[i] = '/'; } @@ -267,7 +267,6 @@ int main(int argc, char * argv []) int calibrate; /* 1=calibrate RC oscillator, 0=don't */ char * port; /* device port (/dev/xxx) */ int terminal; /* 1=enter terminal mode, 0=don't */ - int verify; /* perform a verify operation */ char * exitspecs; /* exit specs string from command line */ char * programmer; /* programmer id */ char * partdesc; /* part id */ @@ -284,7 +283,7 @@ int main(int argc, char * argv []) int init_ok; /* Device initialization worked well */ int is_open; /* Device open succeeded */ char * logfile; /* Use logfile rather than stderr for diagnostics */ - enum updateflags uflags = UF_AUTO_ERASE; /* Flags for do_op() */ + enum updateflags uflags = UF_AUTO_ERASE | UF_VERIFY; /* Flags for do_op() */ #if !defined(WIN32) char * homedir; @@ -349,7 +348,6 @@ int main(int argc, char * argv []) p = NULL; ovsigck = 0; terminal = 0; - verify = 1; /* on by default */ quell_progress = 0; exitspecs = NULL; pgm = NULL; @@ -525,12 +523,6 @@ int main(int argc, char * argv []) exit(1); } ladd(updates, upd); - - if (verify && upd->op == DEVICE_WRITE) { - upd = dup_update(upd); - upd->op = DEVICE_VERIFY; - ladd(updates, upd); - } break; case 'v': @@ -538,7 +530,7 @@ int main(int argc, char * argv []) break; case 'V': - verify = 0; + uflags &= ~UF_VERIFY; break; case 'x': @@ -759,6 +751,7 @@ int main(int argc, char * argv []) bitclock = default_bitclock; } + avrdude_message(MSG_NOTICE, "\n"); // developer option -p /[*codws] prints various aspects of part descriptions and exits @@ -926,6 +919,12 @@ int main(int argc, char * argv []) exit(1); } } + + if (!avr_mem_might_be_known(upd->memtype)) { + avrdude_message(MSG_INFO, "%s: unknown memory type %s\n", progname, upd->memtype); + exit(1); + } + // TODO: check whether filename other than "-" is readable/writable } /* @@ -1251,7 +1250,7 @@ int main(int argc, char * argv []) for (ln=lfirst(updates); ln; ln=lnext(ln)) { upd = ldata(ln); rc = do_op(pgm, p, upd, uflags); - if (rc) { + if (rc && rc != LIBAVRDUDE_SOFTFAIL) { exitrc = 1; break; } diff --git a/src/update.c b/src/update.c index 1840f81f..025bfb6b 100644 --- a/src/update.c +++ b/src/update.c @@ -45,7 +45,7 @@ UPDATE * parse_op(char * s) i = 0; p = s; - while ((i < (sizeof(buf)-1) && *p && (*p != ':'))) + while (i < (int) sizeof(buf)-1 && *p && *p != ':') buf[i++] = *p++; buf[i] = 0; @@ -214,18 +214,124 @@ void free_update(UPDATE * u) } +// Memory statistics considering holes after a file read returned size bytes +int memstats(struct avrpart *p, char *memtype, int size, Filestats *fsp) { + Filestats ret = { 0 }; + AVRMEM *mem = avr_locate_mem(p, memtype); + + if(!mem) { + avrdude_message(MSG_INFO, "%s: %s %s undefined\n", + progname, p->desc, memtype); + return LIBAVRDUDE_GENERAL_FAILURE; + } + + if(!mem->buf || !mem->tags) { + avrdude_message(MSG_INFO, "%s: %s %s is not set\n", + progname, p->desc, memtype); + return LIBAVRDUDE_GENERAL_FAILURE; + } + + int pgsize = mem->page_size; + if(pgsize < 1) + pgsize = 1; + + if(size < 0 || size > mem->size) { + avrdude_message(MSG_INFO, "%s: memstats() size %d at odds with %s %s size %d\n", + progname, size, p->desc, memtype, mem->size); + return LIBAVRDUDE_GENERAL_FAILURE; + } + + ret.lastaddr = -1; + int firstset = 0, insection = 0; + // Scan all memory + for(int addr = 0; addr < mem->size; ) { + int pageset = 0; + // Go page by page + for(int pgi = 0; pgi < pgsize; pgi++, addr++) { + if(mem->tags[addr] & TAG_ALLOCATED) { + if(!firstset) { + firstset = 1; + ret.firstaddr = addr; + } + ret.lastaddr = addr; + // size can be smaller than tags suggest owing to flash trailing-0xff + if(addr < size) { + ret.nbytes++; + if(!pageset) { + pageset = 1; + ret.nfill += pgi; + ret.npages++; + } + if(!insection) { + insection = 1; + ret.nsections++; + } + } else { // Now beyond size returned by input file read + ret.ntrailing++; + if(pageset) + ret.nfill++; + } + } else { // In a hole or beyond input file + insection = 0; + if(pageset) + ret.nfill++; + } + } + } + + if(fsp) + *fsp = ret; + + return LIBAVRDUDE_SUCCESS; +} + + +// Convenience functions for printing +const char *plural(int x) { + return x==1? "": "s"; +} + +const char *inname(const char *fn) { + return !fn? "???": strcmp(fn, "-")? fn: ""; +} + +const char *outname(const char *fn) { + return !fn? "???": strcmp(fn, "-")? fn: ""; +} + +// Return sth like "[0, 0x1ff]" +const char *interval(int a, int b) { + // Cyclic buffer for 20+ temporary interval strings each max 41 bytes at 64-bit int + static char space[20*41 + 80], *sp; + if(!sp || sp-space > (int) sizeof space - 80) + sp = space; + + char *ret = sp; + + sprintf(sp, a<16? "[%d": "[0x%x", a); + sp += strlen(sp); + sprintf(sp, b<16? ", %d]": ", 0x%x]", b); + + // Advance beyond return string in temporary ring buffer + sp += strlen(sp)+1; + + return ret; +} + + int do_op(PROGRAMMER * pgm, struct avrpart * p, UPDATE * upd, enum updateflags flags) { struct avrpart * v; AVRMEM * mem; - int size, vsize; + int size; int rc; + Filestats fs; mem = avr_locate_mem(p, upd->memtype); if (mem == NULL) { - avrdude_message(MSG_INFO, "\"%s\" memory type not defined for part \"%s\"\n", - upd->memtype, p->desc); - return -1; + avrdude_message(MSG_INFO, "%s: skipping -U %s:... as memory not defined for part %s\n", + progname, upd->memtype, p->desc); + return LIBAVRDUDE_SOFTFAIL; } AVRMEM_ALIAS * alias_mem = avr_find_memalias(p, mem); @@ -235,167 +341,189 @@ int do_op(PROGRAMMER * pgm, struct avrpart * p, UPDATE * upd, enum updateflags f strcat(alias_mem_desc, alias_mem->desc); } - if (upd->op == DEVICE_READ) { - /* - * read out the specified device memory and write it to a file - */ + switch (upd->op) { + case DEVICE_READ: + // Read out the specified device memory and write it to a file if (upd->format == FMT_IMM) { avrdude_message(MSG_INFO, - "%s: Invalid file format 'immediate' for output\n", - progname, upd->filename); - return -1; + "%s: Invalid file format 'immediate' for output\n", progname); + return LIBAVRDUDE_GENERAL_FAILURE; } - if (quell_progress < 2) { - avrdude_message(MSG_INFO, "%s: reading %s%s memory:\n", - progname, mem->desc, alias_mem_desc); - } - report_progress(0,1,"Reading"); + if (quell_progress < 2) + avrdude_message(MSG_INFO, "%s: reading %s%s memory ...\n", + progname, mem->desc, alias_mem_desc); + + report_progress(0, 1, "Reading"); rc = avr_read(pgm, p, upd->memtype, 0); + report_progress(1, 1, NULL); if (rc < 0) { avrdude_message(MSG_INFO, "%s: failed to read all of %s%s memory, rc=%d\n", - progname, mem->desc, alias_mem_desc, rc); - return -1; + progname, mem->desc, alias_mem_desc, rc); + return LIBAVRDUDE_GENERAL_FAILURE; } - report_progress(1,1,NULL); size = rc; if (quell_progress < 2) { if (rc == 0) - avrdude_message(MSG_INFO, "%s: Flash is empty, resulting file has no contents.\n", - progname); - avrdude_message(MSG_INFO, "%s: writing output file \"%s\"\n", - progname, - strcmp(upd->filename, "-")==0 ? "" : upd->filename); + avrdude_message(MSG_INFO, "%s: flash is empty, resulting file has no contents\n", + progname); + avrdude_message(MSG_INFO, "%s: writing output file %s\n", + progname, outname(upd->filename)); } rc = fileio(FIO_WRITE, upd->filename, upd->format, p, upd->memtype, size); if (rc < 0) { - avrdude_message(MSG_INFO, "%s: write to file '%s' failed\n", - progname, upd->filename); - return -1; - } - } - else if (upd->op == DEVICE_WRITE) { - /* - * write the selected device memory using data from a file; first - * read the data from the specified file - */ - if (quell_progress < 2) { - avrdude_message(MSG_INFO, "%s: reading input file \"%s\"\n", - progname, - strcmp(upd->filename, "-")==0 ? "" : upd->filename); + avrdude_message(MSG_INFO, "%s: write to file %s failed\n", + progname, outname(upd->filename)); + return LIBAVRDUDE_GENERAL_FAILURE; } + break; + + case DEVICE_WRITE: + // Write the selected device memory using data from a file + rc = fileio(FIO_READ, upd->filename, upd->format, p, upd->memtype, -1); + if (quell_progress < 2) + avrdude_message(MSG_INFO, "%s: reading input file %s for %s%s\n", + progname, inname(upd->filename), mem->desc, alias_mem_desc); if (rc < 0) { - avrdude_message(MSG_INFO, "%s: read from file '%s' failed\n", - progname, upd->filename); - return -1; + avrdude_message(MSG_INFO, "%s: read from file %s failed\n", + progname, inname(upd->filename)); + return LIBAVRDUDE_GENERAL_FAILURE; } size = rc; - /* - * write the buffer contents to the selected memory type - */ - if (quell_progress < 2) { - avrdude_message(MSG_INFO, "%s: writing %s%s (%d bytes):\n", - progname, mem->desc, alias_mem_desc, size); - } + if(memstats(p, upd->memtype, size, &fs) < 0) + return LIBAVRDUDE_GENERAL_FAILURE; + + if(quell_progress < 2) { + int level = fs.nsections > 1 || fs.firstaddr > 0 || fs.ntrailing? MSG_INFO: MSG_NOTICE; + + avrdude_message(level, "%*s with %d byte%s in %d section%s within %s\n", + (int) strlen(progname)+1, "", + fs.nbytes, plural(fs.nbytes), + fs.nsections, plural(fs.nsections), + interval(fs.firstaddr, fs.lastaddr)); + if(mem->page_size > 1) { + avrdude_message(level, "%*s using %d page%s and %d pad byte%s", + (int) strlen(progname)+1, "", + fs.npages, plural(fs.npages), + fs.nfill, plural(fs.nfill)); + if(fs.ntrailing) + avrdude_message(level, ", cutting off %d trailing 0xff byte%s", + fs.ntrailing, plural(fs.ntrailing)); + avrdude_message(level, "\n"); + } + } + + // Write the buffer contents to the selected memory type + if (quell_progress < 2) + avrdude_message(MSG_INFO, "%s: writing %d byte%s %s%s ...\n", + progname, fs.nbytes, plural(fs.nbytes), mem->desc, alias_mem_desc); if (!(flags & UF_NOWRITE)) { - report_progress(0,1,"Writing"); + report_progress(0, 1, "Writing"); rc = avr_write(pgm, p, upd->memtype, size, (flags & UF_AUTO_ERASE) != 0); - report_progress(1,1,NULL); - } - else { - /* - * test mode, don't actually write to the chip, output the buffer - * to stdout in intel hex instead - */ + report_progress(1, 1, NULL); + } else { + // Test mode: write to stdout in intel hex rather than to the chip rc = fileio(FIO_WRITE, "-", FMT_IHEX, p, upd->memtype, size); } if (rc < 0) { avrdude_message(MSG_INFO, "%s: failed to write %s%s memory, rc=%d\n", - progname, mem->desc, alias_mem_desc, rc); - return -1; + progname, mem->desc, alias_mem_desc, rc); + return LIBAVRDUDE_GENERAL_FAILURE; } - vsize = rc; + if (quell_progress < 2) + avrdude_message(MSG_INFO, "%s: %d byte%s of %s%s written\n", + progname, fs.nbytes, plural(fs.nbytes), mem->desc, alias_mem_desc); - if (quell_progress < 2) { - avrdude_message(MSG_INFO, "%s: %d bytes of %s%s written\n", progname, - vsize, mem->desc, alias_mem_desc); - } + // Fall through for (default) auto verify, ie, unless -V was specified + if (!(flags & UF_VERIFY)) + break; - } - else if (upd->op == DEVICE_VERIFY) { - /* - * verify that the in memory file (p->mem[AVR_M_FLASH|AVR_M_EEPROM]) - * is the same as what is on the chip - */ + case DEVICE_VERIFY: + // Verify that the in memory file is the same as what is on the chip pgm->vfy_led(pgm, ON); + int userverify = upd->op == DEVICE_VERIFY; // Explicit -U :v by user + if (quell_progress < 2) { - avrdude_message(MSG_INFO, "%s: verifying %s%s memory against %s:\n", - progname, mem->desc, alias_mem_desc, upd->filename); + avrdude_message(MSG_INFO, "%s: verifying %s%s memory against %s\n", + progname, mem->desc, alias_mem_desc, inname(upd->filename)); - avrdude_message(MSG_NOTICE2, "%s: load data %s%s data from input file %s:\n", - progname, mem->desc, alias_mem_desc, upd->filename); + if (userverify) + avrdude_message(MSG_NOTICE, "%s: load %s%s data from input file %s\n", + progname, mem->desc, alias_mem_desc, inname(upd->filename)); } - rc = fileio(FIO_READ_FOR_VERIFY, upd->filename, upd->format, p, upd->memtype, -1); - if (rc < 0) { - avrdude_message(MSG_INFO, "%s: read from file '%s' failed\n", - progname, upd->filename); - return -1; + // No need to read file when fallen through from DEVICE_WRITE + if (userverify) { + rc = fileio(FIO_READ_FOR_VERIFY, upd->filename, upd->format, p, upd->memtype, -1); + + if (rc < 0) { + avrdude_message(MSG_INFO, "%s: read from file %s failed\n", + progname, inname(upd->filename)); + return LIBAVRDUDE_GENERAL_FAILURE; + } + size = rc; + + if(memstats(p, upd->memtype, size, &fs) < 0) + return LIBAVRDUDE_GENERAL_FAILURE; + } else { + // Correct size of last read to include potentially cut off, trailing 0xff (flash) + size = fs.lastaddr+1; } + v = avr_dup_part(p); - size = rc; + if (quell_progress < 2) { - avrdude_message(MSG_NOTICE2, "%s: input file %s contains %d bytes\n", - progname, upd->filename, size); - avrdude_message(MSG_NOTICE2, "%s: reading on-chip %s%s data:\n", - progname, mem->desc, alias_mem_desc); + if (userverify) + avrdude_message(MSG_NOTICE, "%s: input file %s contains %d byte%s\n", + progname, inname(upd->filename), fs.nbytes, plural(fs.nbytes)); + avrdude_message(MSG_NOTICE2, "%s: reading on-chip %s%s data ...\n", + progname, mem->desc, alias_mem_desc); } report_progress (0,1,"Reading"); rc = avr_read(pgm, p, upd->memtype, v); + report_progress (1,1,NULL); if (rc < 0) { avrdude_message(MSG_INFO, "%s: failed to read all of %s%s memory, rc=%d\n", - progname, mem->desc, alias_mem_desc, rc); + progname, mem->desc, alias_mem_desc, rc); pgm->err_led(pgm, ON); avr_free_part(v); - return -1; + return LIBAVRDUDE_GENERAL_FAILURE; } - report_progress (1,1,NULL); - - - if (quell_progress < 2) { + if (quell_progress < 2) avrdude_message(MSG_NOTICE2, "%s: verifying ...\n", progname); - } + rc = avr_verify(p, v, upd->memtype, size); if (rc < 0) { avrdude_message(MSG_INFO, "%s: verification error; content mismatch\n", - progname); + progname); pgm->err_led(pgm, ON); avr_free_part(v); - return -1; + return LIBAVRDUDE_GENERAL_FAILURE; } if (quell_progress < 2) { - avrdude_message(MSG_INFO, "%s: %d bytes of %s%s verified\n", - progname, rc, mem->desc, alias_mem_desc); + int verified = fs.nbytes+fs.ntrailing; + avrdude_message(MSG_INFO, "%s: %d byte%s of %s%s verified\n", + progname, verified, plural(verified), mem->desc, alias_mem_desc); } pgm->vfy_led(pgm, OFF); avr_free_part(v); - } - else { + break; + + default: avrdude_message(MSG_INFO, "%s: invalid update operation (%d) requested\n", - progname, upd->op); - return -1; + progname, upd->op); + return LIBAVRDUDE_GENERAL_FAILURE; } - return 0; + return LIBAVRDUDE_SUCCESS; } -