748 lines
26 KiB
C
748 lines
26 KiB
C
/*
|
|
* avrdude - A Downloader/Uploader for AVR device programmers
|
|
* Copyright (C) 2022 Stefan Rueger <stefan.rueger@urclocks.c>
|
|
*
|
|
* 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, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
/* $Id$ */
|
|
|
|
#include "ac_cfg.h"
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <string.h>
|
|
#include <sys/time.h>
|
|
#include <time.h>
|
|
|
|
#include "avrdude.h"
|
|
#include "libavrdude.h"
|
|
#include "avrintel.h"
|
|
|
|
/*
|
|
* Provides an API for cached bytewise access
|
|
*
|
|
* int avr_read_byte_cached(const PROGRAMMER *pgm, const AVRPART *p, const
|
|
* AVRMEM *mem, unsigned long addr, unsigned char *value);
|
|
*
|
|
* int avr_write_byte_cached(const PROGRAMMER *pgm, const AVRPART *p, const
|
|
* AVRMEM *mem, unsigned long addr, unsigned char data);
|
|
*
|
|
* int avr_flush_cache(const PROGRAMMER *pgm, const AVRPART *p);
|
|
*
|
|
* int avr_chip_erase_cached(const PROGRAMMER *pgm, const AVRPART *p);
|
|
*
|
|
* int avr_page_erase_cached(const PROGRAMMER *pgm, const AVRPART *p, const
|
|
* AVRMEM *mem, unsigned int baseaddr);
|
|
*
|
|
* int avr_reset_cache(const PROGRAMMER *pgm, const AVRPART *p);
|
|
*
|
|
* avr_read_byte_cached() and avr_write_byte_cached() use a cache if paged
|
|
* routines are available and if the device memory is EEPROM or flash,
|
|
* otherwise they fall back to pgm->read_byte() and pgm->write_byte(),
|
|
* respectively. Bytewise cached read always gets its data from the cache,
|
|
* possibly after reading a page from the device memory. Bytewise cached
|
|
* write with an address in memory range only ever modifies the cache. Any
|
|
* modifications are written to the device after calling avr_flush_cache() or
|
|
* when attempting to read or write from a location outside the address range
|
|
* of the device memory.
|
|
*
|
|
* avr_flush_cache() synchronises pending writes to EEPROM and flash with the
|
|
* device. With some programmer and part combinations, flash (and sometimes
|
|
* EEPROM, too) looks like a NOR memory, ie, one can only write 0 bits, not 1
|
|
* bits. When this is detected, either page erase is deployed (eg, with parts
|
|
* that have PDI/UPDI interfaces), or if that is not available, both EEPROM
|
|
* and flash caches are fully read in, a pgm->chip_erase() command is issued
|
|
* and both EEPROM and flash are written back to the device. Hence, it can
|
|
* take minutes to ensure that a single previously cleared bit is set and,
|
|
* therefore, this routine should be called sparingly.
|
|
*
|
|
* avr_chip_erase_cached() erases the chip and discards pending writes() to
|
|
* flash or EEPROM. It presets the flash cache to all 0xff alleviating the
|
|
* need to read from the device flash. However, if the programmer serves
|
|
* bootloaders (pgm->prog_modes & PM_SPM) then the flash cache is reset
|
|
* instead, necessitating flash memory be fetched from the device on first
|
|
* read; the reason for this is that bootloaders emulate chip erase and they
|
|
* won't overwrite themselves (some bootloaders, eg, optiboot ignore chip
|
|
* erase commands) making it truly unknowable what the flash contents on
|
|
* device is after a chip erase.
|
|
*
|
|
* For EEPROM avr_chip_erase_cached() concludes that it has been deleted if a
|
|
* previously cached EEPROM page that contained cleared bits now no longer
|
|
* has these clear bits on the device. Only with this evidence is the EEPROM
|
|
* cache preset to all 0xff otherwise the cache discards all pending writes
|
|
* to EEPROM and is left unchanged otherwise.
|
|
*
|
|
* The avr_page_erase_cached() function erases a page and synchronises it
|
|
* with the cache.
|
|
*
|
|
* Finally, avr_reset_cache() resets the cache without synchronising pending
|
|
* writes() to the device.
|
|
*
|
|
* This file also holds the following utility functions
|
|
*
|
|
* // Does the programmer/memory combo have paged memory access?
|
|
* int avr_has_paged_access(const PROGRAMMER *pgm, const AVRMEM *mem);
|
|
*
|
|
* // Read the page containing addr from the device into buf
|
|
* int avr_read_page_default(const PROGRAMMER *pgm, const AVRPART *p, const AVRMEM *mem, int addr, unsigned char *buf);
|
|
*
|
|
* // Write the data page to the device into the page containing addr
|
|
* int avr_write_page_default(const PROGRAMMER *pgm, const AVRPART *p, const AVRMEM *mem, int addr, unsigned char *data);
|
|
*
|
|
* // Could memory region s1 be the result of a NOR-memory copy of s3 onto s2?
|
|
* int avr_is_and(const unsigned char *s1, const unsigned char *s2, const unsigned char *s3, size_t n);
|
|
*
|
|
*/
|
|
|
|
|
|
/*
|
|
* Paged access?
|
|
* - Programmer must have paged routines
|
|
* - Memory has positive page size, which is a power of two
|
|
* - Memory has positive size, which is a multiple of the page size
|
|
* - Memory is flash type or eeprom type
|
|
*
|
|
* Note that in this definition the page size can be 1
|
|
*/
|
|
|
|
int avr_has_paged_access(const PROGRAMMER *pgm, const AVRMEM *mem) {
|
|
return pgm->paged_load && pgm->paged_write &&
|
|
mem->page_size > 0 && (mem->page_size & (mem->page_size-1)) == 0 &&
|
|
mem->size > 0 && mem->size % mem->page_size == 0 &&
|
|
(avr_mem_is_flash_type(mem) || avr_mem_is_eeprom_type(mem));
|
|
}
|
|
|
|
|
|
#define fallback_read_byte (pgm->read_byte != avr_read_byte_cached? pgm->read_byte: avr_read_byte_default)
|
|
#define fallback_write_byte (pgm->write_byte != avr_write_byte_cached? pgm->write_byte: avr_write_byte_default)
|
|
|
|
/*
|
|
* Read the page containing addr from the device into buf
|
|
* - Caller to ensure buf has mem->page_size bytes
|
|
* - Part memory buffer mem is unaffected by this (though temporarily changed)
|
|
* - Uses read_byte() if memory page size is one, otherwise paged_load()
|
|
* - Fall back to bytewise read if paged_load() returned an error
|
|
*/
|
|
int avr_read_page_default(const PROGRAMMER *pgm, const AVRPART *p, const AVRMEM *mem, int addr, unsigned char *buf) {
|
|
if(!avr_has_paged_access(pgm, mem) || addr < 0 || addr >= mem->size)
|
|
return LIBAVRDUDE_GENERAL_FAILURE;
|
|
|
|
int rc, pgsize = mem->page_size, base = addr & ~(pgsize-1);
|
|
unsigned char *pagecopy = cfg_malloc("avr_read_page_default()", pgsize);
|
|
|
|
if(pgsize == 1)
|
|
return fallback_read_byte(pgm, p, mem, addr, buf);
|
|
|
|
memcpy(pagecopy, mem->buf + base, pgsize);
|
|
if((rc = pgm->paged_load(pgm, p, mem, pgsize, base, pgsize)) >= 0)
|
|
memcpy(buf, mem->buf + base, pgsize);
|
|
memcpy(mem->buf + base, pagecopy, pgsize);
|
|
|
|
if(rc < 0 && pgm->read_byte != avr_read_byte_cached) {
|
|
rc = LIBAVRDUDE_SUCCESS;
|
|
for(int i=0; i<pgsize; i++) {
|
|
if(pgm->read_byte(pgm, p, mem, base+i, pagecopy+i) < 0) {
|
|
rc = LIBAVRDUDE_GENERAL_FAILURE;
|
|
break;
|
|
}
|
|
}
|
|
if(rc == LIBAVRDUDE_SUCCESS)
|
|
memcpy(buf, pagecopy, pgsize);
|
|
}
|
|
free(pagecopy);
|
|
|
|
return rc;
|
|
}
|
|
|
|
|
|
/*
|
|
* Write the data page to the device into the page containing addr
|
|
* - Caller to provide all mem->page_size bytes incl padding if any
|
|
* - Part memory buffer mem is unaffected by this (though temporarily changed)
|
|
* - Uses write_byte() if memory page size is one, otherwise paged_write()
|
|
*/
|
|
int avr_write_page_default(const PROGRAMMER *pgm, const AVRPART *p, const AVRMEM *mem, int addr, unsigned char *data) {
|
|
if(!avr_has_paged_access(pgm, mem) || addr < 0 || addr >= mem->size)
|
|
return LIBAVRDUDE_GENERAL_FAILURE;
|
|
|
|
int rc, pgsize = mem->page_size, base = addr & ~(pgsize-1);
|
|
unsigned char *pagecopy = cfg_malloc("avr_write_page_default()", pgsize);
|
|
|
|
if(pgsize == 1)
|
|
return fallback_write_byte(pgm, p, mem, addr, *data);
|
|
|
|
memcpy(pagecopy, mem->buf + base, pgsize);
|
|
memcpy(mem->buf + base, data, pgsize);
|
|
rc = pgm->paged_write(pgm, p, mem, pgsize, base, pgsize);
|
|
memcpy(mem->buf + base, pagecopy, pgsize);
|
|
free(pagecopy);
|
|
|
|
return rc;
|
|
}
|
|
|
|
|
|
// Could memory region s1 be the result of a NOR-memory copy of s3 onto s2?
|
|
int avr_is_and(const unsigned char *s1, const unsigned char *s2, const unsigned char *s3, size_t n) {
|
|
while(n--)
|
|
if(*s1++ != (*s2++ & *s3++))
|
|
return 0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
static int initCache(AVR_Cache *cp, const PROGRAMMER *pgm, const AVRPART *p) {
|
|
AVRMEM *basemem = avr_locate_mem(p, cp == pgm->cp_flash? "flash": "eeprom");
|
|
|
|
if(!basemem || !avr_has_paged_access(pgm, basemem))
|
|
return LIBAVRDUDE_GENERAL_FAILURE;
|
|
|
|
cp->size = basemem->size;
|
|
cp->page_size = basemem->page_size;
|
|
cp->offset = basemem->offset;
|
|
cp->cont = cfg_malloc("initCache()", cp->size);
|
|
cp->copy = cfg_malloc("initCache()", cp->size);
|
|
cp->iscached = cfg_malloc("initCache()", cp->size/cp->page_size);
|
|
|
|
return LIBAVRDUDE_SUCCESS;
|
|
}
|
|
|
|
|
|
static int cacheAddress(int addr, const AVR_Cache *cp, const AVRMEM *mem) {
|
|
int cacheaddr = addr + (int) (mem->offset - cp->offset);
|
|
|
|
if(cacheaddr < 0 || cacheaddr >= cp->size) { // Should never happen (unless offsets wrong in avrdude.conf)
|
|
pmsg_error("%s cache address 0x%04x out of range [0, 0x%04x]\n", mem->desc, cacheaddr, cp->size-1);
|
|
return LIBAVRDUDE_GENERAL_FAILURE;
|
|
}
|
|
|
|
if(mem->page_size != cp->page_size) { // Should never happen (unless incompatible page sizes in avrdude.conf)
|
|
pmsg_error("%s page size %d incompatible with cache page size %d\n", mem->desc, mem->page_size, cp->page_size);
|
|
return LIBAVRDUDE_GENERAL_FAILURE;
|
|
}
|
|
|
|
return cacheaddr;
|
|
}
|
|
|
|
|
|
static int loadCachePage(AVR_Cache *cp, const PROGRAMMER *pgm, const AVRPART *p, const AVRMEM *mem, int addr, int cacheaddr, int nlOnErr) {
|
|
int pgno = cacheaddr/cp->page_size;
|
|
|
|
if(!cp->iscached[pgno]) {
|
|
// Read cached section from device
|
|
int cachebase = cacheaddr & ~(cp->page_size-1);
|
|
if(avr_read_page_default(pgm, p, mem, addr & ~(cp->page_size-1), cp->cont + cachebase) < 0) {
|
|
report_progress(1, -1, NULL);
|
|
if(nlOnErr && quell_progress)
|
|
msg_info("\n");
|
|
pmsg_error("unable to read %s page at addr 0x%04x\n", mem->desc, addr);
|
|
return LIBAVRDUDE_GENERAL_FAILURE;
|
|
}
|
|
|
|
// Copy last read device page, so we can later check for changes
|
|
memcpy(cp->copy + cachebase, cp->cont + cachebase, cp->page_size);
|
|
cp->iscached[pgno] = 1;
|
|
}
|
|
|
|
return LIBAVRDUDE_SUCCESS;
|
|
}
|
|
|
|
|
|
static int writeCachePage(AVR_Cache *cp, const PROGRAMMER *pgm, const AVRPART *p, const AVRMEM *mem, int base, int nlOnErr) {
|
|
// Write modified page cont to device; if unsuccessful try bytewise access
|
|
if(avr_write_page_default(pgm, p, mem, base, cp->cont + base) < 0) {
|
|
if(pgm->read_byte != avr_read_byte_cached && pgm->write_byte != avr_write_byte_cached) {
|
|
for(int i=0; i < cp->page_size; i++)
|
|
if(cp->cont[base+i] != cp->copy[base+i])
|
|
if(pgm->write_byte(pgm, p, mem, base+i, cp->cont[base+i]) < 0 ||
|
|
pgm->read_byte(pgm, p, mem, base+i, cp->copy+base+i) < 0) {
|
|
report_progress(1, -1, NULL);
|
|
if(nlOnErr && quell_progress)
|
|
msg_info("\n");
|
|
pmsg_error("%s access error at addr 0x%04x\n", mem->desc, base+i);
|
|
return LIBAVRDUDE_GENERAL_FAILURE;
|
|
}
|
|
|
|
return LIBAVRDUDE_SUCCESS; // Bytewise writes & reads successful
|
|
}
|
|
report_progress(1, -1, NULL);
|
|
if(nlOnErr && quell_progress)
|
|
msg_info("\n");
|
|
pmsg_error("write %s page error at addr 0x%04x\n", mem->desc, base);
|
|
return LIBAVRDUDE_GENERAL_FAILURE;
|
|
}
|
|
// Read page back from device and update copy to what is on device
|
|
if(avr_read_page_default(pgm, p, mem, base, cp->copy + base) < 0) {
|
|
report_progress(1, -1, NULL);
|
|
if(nlOnErr && quell_progress)
|
|
msg_info("\n");
|
|
pmsg_error("unable to read %s page at addr 0x%04x\n", mem->desc, base);
|
|
return LIBAVRDUDE_GENERAL_FAILURE;
|
|
}
|
|
|
|
return LIBAVRDUDE_SUCCESS;
|
|
}
|
|
|
|
|
|
// Does the memory region only haxe 0xff?
|
|
static int _is_all_0xff(const void *p, size_t n) {
|
|
const unsigned char *q = (const unsigned char *) p;
|
|
return n <= 0 || (*q == 0xff && memcmp(q, q+1, n-1) == 0);
|
|
}
|
|
|
|
|
|
// A coarse guess where any bootloader might start (prob underestimates the start)
|
|
static int guessBootStart(const PROGRAMMER *pgm, const AVRPART *p) {
|
|
int bootstart = 0;
|
|
const AVR_Cache *cp = pgm->cp_flash;
|
|
|
|
for(size_t i = 0; i < sizeof uP_table/sizeof*uP_table; i++)
|
|
if(p->mcuid == uP_table[i].mcuid) {
|
|
if(uP_table[i].nboots > 0 && uP_table[i].bootsize > 0 && uP_table[i].flashsize == cp->size)
|
|
bootstart = cp->size - uP_table[i].nboots * uP_table[i].bootsize;
|
|
break;
|
|
}
|
|
|
|
if(bootstart <= cp->size/2 || bootstart >= cp->size)
|
|
bootstart = cp->size > 32768? cp->size - 16384: cp->size*3/4;
|
|
|
|
return bootstart & ~(cp->page_size-1);
|
|
}
|
|
|
|
|
|
typedef struct {
|
|
AVRMEM *mem;
|
|
AVR_Cache *cp;
|
|
int isflash, zopaddr, pgerase;
|
|
} CacheDesc_t;
|
|
|
|
|
|
// Write both EEPROM and flash caches to device and free them
|
|
int avr_flush_cache(const PROGRAMMER *pgm, const AVRPART *p) {
|
|
CacheDesc_t mems[2] = {
|
|
{ avr_locate_mem(p, "flash"), pgm->cp_flash, 1, -1, 0 },
|
|
{ avr_locate_mem(p, "eeprom"), pgm->cp_eeprom, 0, -1, 0 },
|
|
};
|
|
|
|
int chpages = 0;
|
|
bool chiperase = 0;
|
|
// Count page changes and find a page that needs a clear bit set
|
|
for(size_t i = 0; i < sizeof mems/sizeof*mems; i++) {
|
|
AVRMEM *mem = mems[i].mem;
|
|
AVR_Cache *cp = mems[i].cp;
|
|
if(!mem || !cp->cont)
|
|
continue;
|
|
|
|
for(int pgno = 0, n = 0; n < cp->size; pgno++, n += cp->page_size) {
|
|
if(cp->iscached[pgno])
|
|
if(memcmp(cp->copy + n, cp->cont + n, cp->page_size)) {
|
|
chpages++;
|
|
if(mems[i].zopaddr == -1 && !avr_is_and(cp->cont + n, cp->copy + n, cp->cont + n, cp->page_size))
|
|
mems[i].zopaddr = n;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(!chpages)
|
|
return LIBAVRDUDE_SUCCESS;
|
|
|
|
pmsg_info("synching cache to device ... ");
|
|
fflush(stderr);
|
|
|
|
// Check whether page erase needed and working and whether chip erase needed
|
|
for(size_t i = 0; i < sizeof mems/sizeof*mems; i++) {
|
|
AVRMEM *mem = mems[i].mem;
|
|
AVR_Cache *cp = mems[i].cp;
|
|
|
|
if(!cp->cont) // Ensure cache is initialised from now on
|
|
if(initCache(cp, pgm, p) < 0) {
|
|
if(quell_progress)
|
|
msg_info("\n");
|
|
pmsg_error("unable to initialise the cache\n");
|
|
return LIBAVRDUDE_GENERAL_FAILURE;
|
|
}
|
|
|
|
if(chiperase || !mem || mems[i].zopaddr < 0)
|
|
continue;
|
|
|
|
int n=mems[i].zopaddr;
|
|
|
|
if(writeCachePage(cp, pgm, p, mem, n, 1) < 0)
|
|
return LIBAVRDUDE_GENERAL_FAILURE;
|
|
// Same? OK, can set cleared bit to one, "normal" memory
|
|
if(!memcmp(cp->copy + n, cp->cont + n, cp->page_size)) {
|
|
chpages--;
|
|
continue;
|
|
}
|
|
|
|
// Probably NOR memory, check out page erase
|
|
if(pgm->page_erase && pgm->page_erase(pgm, p, mem, n) >= 0) {
|
|
if(writeCachePage(cp, pgm, p, mem, n, 1) < 0)
|
|
return LIBAVRDUDE_GENERAL_FAILURE;
|
|
// Worked OK? Can use page erase on this memory
|
|
if(!memcmp(cp->copy + n, cp->cont + n, cp->page_size)) {
|
|
mems[i].pgerase = 1;
|
|
chpages--;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
chiperase = 1;
|
|
}
|
|
|
|
if(!chpages) {
|
|
msg_info("done\n");
|
|
return LIBAVRDUDE_SUCCESS;
|
|
}
|
|
|
|
if(chiperase) {
|
|
if(quell_progress) {
|
|
msg_info("reading/chip erase/writing cycle needed ... ");
|
|
fflush(stderr);
|
|
}
|
|
|
|
int nrd = 0;
|
|
// Count read operations needed
|
|
for(size_t i = 0; i < sizeof mems/sizeof*mems; i++) {
|
|
AVRMEM *mem = mems[i].mem;
|
|
AVR_Cache *cp = mems[i].cp;
|
|
if(!mem)
|
|
continue;
|
|
|
|
for(int pgno = 0, n = 0; n < cp->size; pgno++, n += cp->page_size)
|
|
if(!cp->iscached[pgno])
|
|
nrd++;
|
|
}
|
|
|
|
report_progress(0, 1, "Reading");
|
|
if(nrd) {
|
|
// Read full flash and EEPROM
|
|
for(size_t i = 0; i < sizeof mems/sizeof*mems; i++) {
|
|
AVRMEM *mem = mems[i].mem;
|
|
AVR_Cache *cp = mems[i].cp;
|
|
if(!mem)
|
|
continue;
|
|
|
|
for(int ird = 0, pgno = 0, n = 0; n < cp->size; pgno++, n += cp->page_size) {
|
|
if(!cp->iscached[pgno]) {
|
|
report_progress(ird++, nrd, NULL);
|
|
if(loadCachePage(cp, pgm, p, mem, n, n, 1) < 0)
|
|
return LIBAVRDUDE_GENERAL_FAILURE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
report_progress(1, 0, NULL);
|
|
|
|
report_progress(0, 1, "Erasing");
|
|
if(avr_chip_erase(pgm, p) < 0) {
|
|
report_progress(1, -1, NULL);
|
|
if(quell_progress)
|
|
msg_info("\n");
|
|
pmsg_error("chip erase failed\n");
|
|
return LIBAVRDUDE_GENERAL_FAILURE;
|
|
}
|
|
|
|
// Update cache copies after chip erase so that writing back is efficient
|
|
for(size_t i = 0; i < sizeof mems/sizeof*mems; i++) {
|
|
AVRMEM *mem = mems[i].mem;
|
|
AVR_Cache *cp = mems[i].cp;
|
|
if(!mem)
|
|
continue;
|
|
|
|
if(mems[i].isflash) { // flash
|
|
memset(cp->copy, 0xff, cp->size); // record device memory as erased
|
|
if(pgm->prog_modes & PM_SPM) { // Bootloaders will not overwrite themselves
|
|
// Read back generously estimated bootloader section to avoid verification errors
|
|
int bootstart = guessBootStart(pgm, p);
|
|
int nbo = (cp->size - bootstart)/cp->page_size;
|
|
|
|
for(int ibo = 0, n = bootstart; n < cp->size; n += cp->page_size) {
|
|
report_progress(1+ibo++, nbo+2, NULL);
|
|
if(avr_read_page_default(pgm, p, mem, n, cp->copy + n) < 0) {
|
|
report_progress(1, -1, NULL);
|
|
if(quell_progress)
|
|
msg_info("\n");
|
|
pmsg_error("flash read failed at addr 0x%04x\n", n);
|
|
return LIBAVRDUDE_GENERAL_FAILURE;
|
|
}
|
|
}
|
|
}
|
|
} else { // EEPROM
|
|
// Don't know whether chip erase has zapped EEPROM
|
|
for(int n = 0; n < cp->size; n += cp->page_size) {
|
|
if(!_is_all_0xff(cp->copy + n, cp->page_size)) { // First page that had EEPROM data
|
|
if(avr_read_page_default(pgm, p, mem, n, cp->copy + n) < 0) {
|
|
report_progress(1, -1, NULL);
|
|
if(quell_progress)
|
|
msg_info("\n");
|
|
pmsg_error("EEPROM read failed at addr 0x%04x\n", n);
|
|
return LIBAVRDUDE_GENERAL_FAILURE;
|
|
}
|
|
// EEPROM zapped by chip erase? Set all copy to 0xff
|
|
if(_is_all_0xff(cp->copy + n, cp->page_size))
|
|
memset(cp->copy, 0xff, cp->size);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
report_progress(1, 0, NULL);
|
|
}
|
|
|
|
int nwr = 0;
|
|
// Count number of writes
|
|
for(size_t i = 0; i < sizeof mems/sizeof*mems; i++) {
|
|
AVRMEM *mem = mems[i].mem;
|
|
AVR_Cache *cp = mems[i].cp;
|
|
if(!mem)
|
|
continue;
|
|
|
|
for(int pgno = 0, n = 0; n < cp->size; pgno++, n += cp->page_size)
|
|
if(cp->iscached[pgno] && memcmp(cp->copy + n, cp->cont + n, cp->page_size))
|
|
nwr++;
|
|
}
|
|
|
|
report_progress(0, 1, "Writing");
|
|
if(nwr) {
|
|
// Write all modified pages to the device
|
|
for(size_t i = 0; i < sizeof mems/sizeof*mems; i++) {
|
|
AVRMEM *mem = mems[i].mem;
|
|
AVR_Cache *cp = mems[i].cp;
|
|
if(!mem || !cp->cont)
|
|
continue;
|
|
|
|
for(int iwr = 0, pgno = 0, n = 0; n < cp->size; pgno++, n += cp->page_size) {
|
|
if(cp->iscached[pgno] && memcmp(cp->copy + n, cp->cont + n, cp->page_size)) {
|
|
if(!chiperase && mems[i].pgerase)
|
|
pgm->page_erase(pgm, p, mem, n);
|
|
if(writeCachePage(cp, pgm, p, mem, n, 1) < 0)
|
|
return LIBAVRDUDE_GENERAL_FAILURE;
|
|
if(memcmp(cp->copy + n, cp->cont + n, cp->page_size)) {
|
|
report_progress(1, -1, NULL);
|
|
if(quell_progress)
|
|
msg_info("\n");
|
|
pmsg_error("verification mismatch at %s page addr 0x%04x\n", mem->desc, n);
|
|
return LIBAVRDUDE_GENERAL_FAILURE;
|
|
}
|
|
report_progress(iwr++, nwr, NULL);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
report_progress(1, 0, NULL);
|
|
|
|
msg_info(quell_progress? "done\n": "\n");
|
|
return LIBAVRDUDE_SUCCESS;
|
|
}
|
|
|
|
|
|
/*
|
|
* Read byte via a read/write cache
|
|
* - Used if paged routines available and if memory is EEPROM or flash
|
|
* - Otherwise fall back to pgm->read_byte()
|
|
* - Out of memory addr: synchronise cache and, if successful, pretend reading a zero
|
|
* - Cache is automagically created and initialised if needed
|
|
*/
|
|
int avr_read_byte_cached(const PROGRAMMER *pgm, const AVRPART *p, const AVRMEM *mem,
|
|
unsigned long addr, unsigned char *value) {
|
|
|
|
// Use pgm->read_byte() if not EEPROM/flash or no paged access
|
|
if(!avr_has_paged_access(pgm, mem))
|
|
return fallback_read_byte(pgm, p, mem, addr, value);
|
|
|
|
// If address is out of range synchronise cache and, if successful, pretend reading a zero
|
|
if(addr >= (unsigned long) mem->size) {
|
|
if(avr_flush_cache(pgm, p) < 0)
|
|
return LIBAVRDUDE_GENERAL_FAILURE;
|
|
*value = 0;
|
|
return LIBAVRDUDE_SUCCESS;
|
|
}
|
|
|
|
AVR_Cache *cp = avr_mem_is_eeprom_type(mem)? pgm->cp_eeprom: pgm->cp_flash;
|
|
|
|
if(!cp->cont) // Init cache if needed
|
|
if(initCache(cp, pgm, p) < 0)
|
|
return LIBAVRDUDE_GENERAL_FAILURE;
|
|
|
|
int cacheaddr = cacheAddress((int) addr, cp, mem);
|
|
if(cacheaddr < 0)
|
|
return LIBAVRDUDE_GENERAL_FAILURE;
|
|
|
|
// Ensure cache page is there
|
|
if(loadCachePage(cp, pgm, p, mem, addr, cacheaddr, 0) < 0)
|
|
return LIBAVRDUDE_GENERAL_FAILURE;
|
|
|
|
*value = cp->cont[cacheaddr];
|
|
|
|
return LIBAVRDUDE_SUCCESS;
|
|
}
|
|
|
|
|
|
/*
|
|
* Write byte via a read/write cache
|
|
* - Used if paged routines available and if memory is EEPROM or flash
|
|
* - Otherwise fall back to pgm->write_byte()
|
|
* - Out of memory addr: synchronise cache with device and return whether successful
|
|
* - Cache is automagically created and initialised if needed
|
|
*/
|
|
int avr_write_byte_cached(const PROGRAMMER *pgm, const AVRPART *p, const AVRMEM *mem,
|
|
unsigned long addr, unsigned char data) {
|
|
|
|
// Use pgm->write_byte() if not EEPROM/flash or no paged access
|
|
if(!avr_has_paged_access(pgm, mem))
|
|
return fallback_write_byte(pgm, p, mem, addr, data);
|
|
|
|
// If address is out of range synchronise caches with device and return whether successful
|
|
if(addr >= (unsigned long) mem->size)
|
|
return avr_flush_cache(pgm, p);
|
|
|
|
AVR_Cache *cp = avr_mem_is_eeprom_type(mem)? pgm->cp_eeprom: pgm->cp_flash;
|
|
|
|
if(!cp->cont) // Init cache if needed
|
|
if(initCache(cp, pgm, p) < 0)
|
|
return LIBAVRDUDE_GENERAL_FAILURE;
|
|
|
|
int cacheaddr = cacheAddress((int) addr, cp, mem);
|
|
if(cacheaddr < 0)
|
|
return LIBAVRDUDE_GENERAL_FAILURE;
|
|
|
|
// Ensure cache page is there
|
|
if(loadCachePage(cp, pgm, p, mem, addr, cacheaddr, 0) < 0)
|
|
return LIBAVRDUDE_GENERAL_FAILURE;
|
|
|
|
cp->cont[cacheaddr] = data;
|
|
|
|
return LIBAVRDUDE_SUCCESS;
|
|
}
|
|
|
|
|
|
// Erase the chip and set the cache accordingly
|
|
int avr_chip_erase_cached(const PROGRAMMER *pgm, const AVRPART *p) {
|
|
CacheDesc_t mems[2] = {
|
|
{ avr_locate_mem(p, "flash"), pgm->cp_flash, 1 },
|
|
{ avr_locate_mem(p, "eeprom"), pgm->cp_eeprom, 0 },
|
|
};
|
|
|
|
if(pgm->chip_erase(pgm, p) < 0)
|
|
return LIBAVRDUDE_GENERAL_FAILURE;
|
|
|
|
for(size_t i = 0; i < sizeof mems/sizeof*mems; i++) {
|
|
AVRMEM *mem = mems[i].mem;
|
|
AVR_Cache *cp = mems[i].cp;
|
|
|
|
if(!mem || !avr_has_paged_access(pgm, mem))
|
|
continue;
|
|
|
|
if(!cp->cont) // Init cache if needed
|
|
if(initCache(cp, pgm, p) < 0)
|
|
return LIBAVRDUDE_GENERAL_FAILURE;
|
|
|
|
if(mems[i].isflash) { // flash
|
|
if(pgm->prog_modes & PM_SPM) { // reset cache to unknown
|
|
memset(cp->iscached, 0, cp->size/cp->page_size);
|
|
} else { // preset all pages as erased
|
|
memset(cp->copy, 0xff, cp->size);
|
|
memset(cp->cont, 0xff, cp->size);
|
|
memset(cp->iscached, 1, cp->size/cp->page_size);
|
|
}
|
|
} else { // EEPROM: test whether cached pages were zapped
|
|
bool erasedee = 0;
|
|
for(int pgno = 0, n = 0; n < cp->size; pgno++, n += cp->page_size) {
|
|
if(cp->iscached[pgno]) {
|
|
if(!_is_all_0xff(cp->copy + n, cp->page_size)) { // Page has EEPROM data?
|
|
if(avr_read_page_default(pgm, p, mem, n, cp->copy + n) < 0)
|
|
return LIBAVRDUDE_GENERAL_FAILURE;
|
|
erasedee = _is_all_0xff(cp->copy + n, cp->page_size);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if(erasedee) { // EEPROM was erased, set cache correspondingly
|
|
memset(cp->copy, 0xff, cp->size);
|
|
memset(cp->cont, 0xff, cp->size);
|
|
memset(cp->iscached, 1, cp->size/cp->page_size);
|
|
} else { // discard previous writes, but leave cache
|
|
for(int pgno = 0, n = 0; n < cp->size; pgno++, n += cp->page_size)
|
|
if(cp->iscached[pgno])
|
|
memcpy(cp->cont + n, cp->copy + n, cp->page_size);
|
|
}
|
|
}
|
|
}
|
|
|
|
return LIBAVRDUDE_SUCCESS;
|
|
}
|
|
|
|
|
|
// Erase a page and synchronise it with the cache
|
|
int avr_page_erase_cached(const PROGRAMMER *pgm, const AVRPART *p, const AVRMEM *mem,
|
|
unsigned int uaddr) {
|
|
|
|
int addr = uaddr;
|
|
|
|
if(!avr_has_paged_access(pgm, mem) || addr < 0 || addr >= mem->size)
|
|
return LIBAVRDUDE_GENERAL_FAILURE;
|
|
|
|
if(mem->page_size == 1) {
|
|
if(fallback_write_byte(pgm, p, mem, uaddr, 0xff) < 0)
|
|
return LIBAVRDUDE_GENERAL_FAILURE;
|
|
} else {
|
|
if(!pgm->page_erase || pgm->page_erase(pgm, p, mem, uaddr) < 0)
|
|
return LIBAVRDUDE_GENERAL_FAILURE;
|
|
}
|
|
|
|
AVR_Cache *cp = avr_mem_is_eeprom_type(mem)? pgm->cp_eeprom: pgm->cp_flash;
|
|
|
|
if(!cp->cont) // Init cache if needed
|
|
if(initCache(cp, pgm, p) < 0)
|
|
return LIBAVRDUDE_GENERAL_FAILURE;
|
|
|
|
int cacheaddr = cacheAddress(addr, cp, mem);
|
|
if(cacheaddr < 0)
|
|
return LIBAVRDUDE_GENERAL_FAILURE;
|
|
|
|
// Invalidate this cache page and read back, ie, we don't trust the page_erase() routine
|
|
cp->iscached[cacheaddr/cp->page_size] = 0;
|
|
|
|
// Reload cache page
|
|
if(loadCachePage(cp, pgm, p, mem, (int) addr, cacheaddr, 0) < 0)
|
|
return LIBAVRDUDE_GENERAL_FAILURE;
|
|
|
|
if(!_is_all_0xff(cp->cont + (cacheaddr & ~(cp->page_size-1)), cp->page_size))
|
|
return LIBAVRDUDE_GENERAL_FAILURE;
|
|
|
|
return LIBAVRDUDE_SUCCESS;
|
|
}
|
|
|
|
|
|
// Free cache(s) discarding any pending writes
|
|
int avr_reset_cache(const PROGRAMMER *pgm, const AVRPART *p) {
|
|
AVR_Cache *mems[2] = { pgm->cp_flash, pgm->cp_eeprom, };
|
|
|
|
for(size_t i = 0; i < sizeof mems/sizeof*mems; i++) {
|
|
AVR_Cache *cp = mems[i];
|
|
if(cp->cont)
|
|
free(cp->cont);
|
|
if(cp->copy)
|
|
free(cp->copy);
|
|
if(cp->iscached)
|
|
free(cp->iscached);
|
|
memset(cp, 0, sizeof*cp);
|
|
}
|
|
|
|
return LIBAVRDUDE_SUCCESS;
|
|
}
|