diff --git a/.gitignore b/.gitignore index e4fbe81..c2c3395 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ twiboot mpmboot funkboot eprom_prog +butterfly_prog diff --git a/Makefile b/Makefile index f9831c2..096ca0d 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -TARGETS=twiboot mpmboot funkboot eprom_prog +TARGETS=twiboot mpmboot funkboot eprom_prog butterfly_prog TARGET_DIR=~/bin BUILD_DIR = build diff --git a/butterfly.c b/butterfly.c new file mode 100644 index 0000000..75cdfd5 --- /dev/null +++ b/butterfly.c @@ -0,0 +1,766 @@ +/*************************************************************************** + * Copyright (C) 01/2020 by Olaf Rempel * + * razzor@kopf-tisch.de * + * * + * 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; version 2 of the License, * + * * + * 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. * + ***************************************************************************/ +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "chipinfo_avr.h" +#include "multiboot.h" +#include "optarg.h" + +#define ARRAY_SIZE(x) (sizeof(x) / sizeof(*x)) +#define MIN(a, b) ((a) < (b) ? (a) : (b)) + +#define SERIAL_BAUDRATE B115200 +#define SERIAL_TIMEOUT 1000 + +struct multiboot_ops butterfly_ops; + +typedef struct bfly_privdata_s +{ + char * p_device; + struct termios oldtio; + int fd; + + uint8_t twi_address; + uint8_t chip_erase; + + uint16_t buffersize; + uint16_t flashsize; + uint16_t eepromsize; + + uint8_t progmode_active; +} bfly_privdata_t; + +static struct option bfly_optargs[] = +{ + { "address", 1, 0, 'a' }, /* [ -a
*/ + { "erase", 0, 0, 'e' }, /* [ -e ] */ +}; + +/* ************************************************************************* + * bfly_optarg_cb + * ************************************************************************* */ +static int bfly_optarg_cb(int val, const char *arg, void *privdata) +{ + bfly_privdata_t * p_priv; + + p_priv = (bfly_privdata_t *)privdata; + + switch (val) + { + case 'a': /* address */ + { + char *endptr; + + p_priv->twi_address = strtol(arg, &endptr, 16); + if ((*endptr != '\0') || + (p_priv->twi_address < 0x01) || + (p_priv->twi_address > 0x7F) + ) + { + fprintf(stderr, "invalid address: '%s'\n", arg); + return -1; + } + } + break; + + case 'd': /* device */ + if (p_priv->p_device != NULL) + { + fprintf(stderr, "invalid device: '%s'\n", arg); + return -1; + } + + p_priv->p_device = strdup(optarg); + if (p_priv->p_device == NULL) + { + perror("strdup()"); + return -1; + } + break; + + case 'e': /* no verify after write */ + p_priv->chip_erase = 1; + break; + + case 'h': + case '?': /* error */ + fprintf(stderr, "Usage: butterfly_prog [options]\n" + " -a
- optional: twi address for twiboot bridge mode\n" + " -d - selects butterfly serial device\n" + " -r : - reads flash/eeprom to file (.bin | .hex | -)\n" + " -w : - write flash/eeprom from file (.bin | .hex)\n" + " -n - disable verify after write\n" + " -p <0|1|2> - progress bar mode\n" + "\n" + "Example: butterfly_prog -d /dev/ttyUSB0 -w flash:code.hex\n" + "\n"); + + return -1; + + default: + return 1; + } + + return 0; +} /* bfly_optarg_cb */ + + +/* ************************************************************************* + * butterfly_alloc + * ************************************************************************* */ +static struct multiboot * butterfly_alloc(void) +{ + struct multiboot * mboot = malloc(sizeof(struct multiboot)); + if (mboot == NULL) + { + return NULL; + } + + memset(mboot, 0x00, sizeof(struct multiboot)); + mboot->ops= &butterfly_ops; + + bfly_privdata_t * p_priv = malloc(sizeof(bfly_privdata_t)); + if (p_priv == NULL) + { + free(mboot); + return NULL; + } + + memset(p_priv, 0x00, sizeof(bfly_privdata_t)); + + optarg_register(bfly_optargs, ARRAY_SIZE(bfly_optargs), + bfly_optarg_cb, (void *)p_priv); + + mboot->privdata = p_priv; + return mboot; +} /* butterfly_alloc */ + + +/* ************************************************************************* + * butterfly_free + * ************************************************************************* */ +static void butterfly_free(struct multiboot * p_mboot) +{ + bfly_privdata_t * p_priv = (bfly_privdata_t *)p_mboot->privdata; + + if (p_priv->p_device != NULL) + { + free(p_priv->p_device); + } + + free(p_priv); + free(p_mboot); +} /* butterfly_free */ + + +/* ************************************************************************* + * butterfly_get_memtype + * ************************************************************************* */ +static int butterfly_get_memtype(struct multiboot * p_mboot, + const char * p_memname) +{ + /* unused parameter */ + (void)p_mboot; + + if (strcmp(p_memname, "flash") == 0) + { + return 'F'; + } + else if (strcmp(p_memname, "eeprom") == 0) + { + return 'E'; + } + + return -1; +} /* butterfly_get_memtype */ + + +/* ************************************************************************* + * butterfly_get_memsize + * ************************************************************************* */ +static int butterfly_get_memsize(struct multiboot * p_mboot, + int memtype) +{ + bfly_privdata_t * p_priv = (bfly_privdata_t *)p_mboot->privdata; + + if (!p_priv->progmode_active) + { + return 0; + } + + switch (memtype) + { + case 'F': + return p_priv->flashsize; + + case 'E': + return p_priv->eepromsize; + + default: + return 0; + } +} /* butterfly_get_memsize */ + + +/* ************************************************************************* + * butterfly_close_device + * ************************************************************************* */ +static void butterfly_close_device(bfly_privdata_t * p_priv) +{ + /* delay close() / tcsetattr() */ + usleep(100000); + + tcsetattr(p_priv->fd, TCSANOW, &p_priv->oldtio); + close(p_priv->fd); +} /* butterfly_close_device */ + + +/* ************************************************************************* + * butterlfy_open_device + * ************************************************************************* */ +static int butterfly_open_device(bfly_privdata_t * p_priv) +{ + p_priv->fd = open(p_priv->p_device, O_RDWR | O_NOCTTY | O_CLOEXEC); + if (p_priv->fd < 0) + { + perror("open()"); + return -1; + } + + if (tcgetattr(p_priv->fd, &p_priv->oldtio) < 0) + { + perror("tcgetattr(oldtio)"); + close(p_priv->fd); + return -1; + } + + struct termios newtio; + memset(&newtio, 0, sizeof(newtio)); + + newtio.c_iflag |= IGNBRK; + newtio.c_cflag |= SERIAL_BAUDRATE | CS8 | CLOCAL | CREAD; + + newtio.c_cc[VMIN] = 1; + newtio.c_cc[VTIME] = 0; + + int err = tcsetattr(p_priv->fd, TCSANOW, &newtio); + if (err < 0) + { + perror("tcsetattr(newtio)"); + close(p_priv->fd); + return -1; + } + + return 0; +} /* butterlfy_open_device */ + + +/* ************************************************************************* + * butterfly_serial_read + * ************************************************************************* */ +static int butterfly_serial_read(int fd, void * data, int size, + unsigned int timeout_ms) +{ + int pos = 0; + + while (1) + { + fd_set fdset; + struct timeval timeout; + struct timeval * p_timeout = NULL; + + FD_ZERO(&fdset); + FD_SET(fd, &fdset); + + if (timeout_ms != 0) + { + p_timeout = &timeout; + + timeout.tv_sec = timeout_ms / 1000; + timeout.tv_usec = (timeout_ms % 1000) * 1000; + } + + int ret = select(fd +1, &fdset, NULL, NULL, p_timeout); + if (ret == -1) + { + perror("select"); + return -1; + } + else if (ret == 0) + { + break; + } + else if (FD_ISSET(fd, &fdset)) + { + int len = read(fd, data + pos, size - pos); + if (len < 0) + { + return -1; + } + else + { + pos += len; + if (pos == size) + { + break; + } + } + } + } + + return pos; +} /* butterfly_serial_read */ + + +/* ************************************************************************* + * butterfly_expect_cr + * ************************************************************************* */ +static int butterfly_expect_cr(bfly_privdata_t * p_priv) +{ + uint8_t buffer[1]; + int result; + + result = butterfly_serial_read(p_priv->fd, buffer, sizeof(buffer), + SERIAL_TIMEOUT); + if ((result == sizeof(buffer)) && + (buffer[0] == '\r') + ) + { + return 0; + } + + return -1; +} /* butterfly_expect_cr */ + + +/* ************************************************************************* + * butterfly_enter_progmode + * ************************************************************************* */ +static int butterfly_enter_progmode(bfly_privdata_t * p_priv) +{ + if (p_priv->twi_address == 0x00) + { + (void)write(p_priv->fd, "P", 1); + } + else + { + uint8_t cmd[2] = { 'I' , p_priv->twi_address }; + (void)write(p_priv->fd, cmd, 2); + } + + return butterfly_expect_cr(p_priv); +} /* butterfly_enter_progmode */ + + +/* ************************************************************************* + * butterfly_leave_progmode + * ************************************************************************* */ +static int butterfly_leave_progmode(bfly_privdata_t * p_priv) +{ + (void)write(p_priv->fd, "L", 1); + + return butterfly_expect_cr(p_priv); +} /* butterfly_leave_progmode */ + + +/* ************************************************************************* + * butterfly_get_signature + * ************************************************************************* */ +static int butterfly_get_signature(bfly_privdata_t * p_priv, + uint8_t * p_signature) +{ + int result; + uint8_t buffer[3]; + + (void)write(p_priv->fd, "s", 1); + + result = butterfly_serial_read(p_priv->fd, buffer, sizeof(buffer), + SERIAL_TIMEOUT); + + if (result == 3) + { + p_signature[0] = buffer[2]; + p_signature[1] = buffer[1]; + p_signature[2] = buffer[0]; + return 0; + } + + return -1; +} /* butterfly_get_signature */ + + +/* ************************************************************************* + * butterfly_get_buffersize + * ************************************************************************* */ +static int butterfly_get_buffersize(bfly_privdata_t * p_priv, + uint16_t * p_buffersize) +{ + int result; + uint8_t buffer[3]; + + (void)write(p_priv->fd, "b", 1); + + result = butterfly_serial_read(p_priv->fd, buffer, sizeof(buffer), + SERIAL_TIMEOUT); + if (result == sizeof(buffer)) + { + if (buffer[0] == 'Y') + { + *p_buffersize = (buffer[1] << 8) | buffer[2]; + } + else + { + *p_buffersize = 0; + } + } + + return (result != sizeof(buffer)); +} /* butterfly_get_buffersize */ + + +/* ************************************************************************* + * butterfly_chiperase + * ************************************************************************* */ +static int butterfly_chiperase(bfly_privdata_t * p_priv) +{ + (void)write(p_priv->fd, "e", 1); + + return butterfly_expect_cr(p_priv); +} /* butterfly_chiperase */ + + +/* ************************************************************************* + * butterfly_set_address + * ************************************************************************* */ +static int butterfly_set_address(bfly_privdata_t * p_priv, uint16_t pos) +{ + uint8_t buffer[1]; + int result; + + (void)write(p_priv->fd, "a", 1); + + result = butterfly_serial_read(p_priv->fd, buffer, sizeof(buffer), + SERIAL_TIMEOUT); + if ((result == 1) && + (buffer[0] == 'Y') + ) + { + /* convert to word address */ + pos >>= 1; + + uint8_t cmd[3] = { 'A', pos >> 8, pos & 0xFF }; + (void)write(p_priv->fd, cmd, sizeof(cmd)); + + result = butterfly_expect_cr(p_priv); + } + + return result; +} /* butterfly_set_address */ + + +/* ************************************************************************* + * butterfly_read_data + * ************************************************************************* */ +static int butterfly_read_data(bfly_privdata_t * p_priv, + uint8_t * p_data, uint16_t size, + uint8_t memtype) +{ + int result; + + uint8_t cmd[4] = { 'g', size >> 8, size & 0xFF, memtype }; + (void)write(p_priv->fd, cmd, 4); + + result = butterfly_serial_read(p_priv->fd, p_data, size, + SERIAL_TIMEOUT); + return (result != size); +} /* butterfly_read_data */ + + +/* ************************************************************************* + * butterfly_write_data + * ************************************************************************* */ +static int butterfly_write_data(bfly_privdata_t * p_priv, + const uint8_t * p_data, uint16_t size, + uint8_t memtype) +{ + uint8_t cmd[4] = { 'B', size >> 8, size & 0xFF, memtype }; + (void)write(p_priv->fd, cmd, 4); + + (void)write(p_priv->fd, p_data, size); + + return butterfly_expect_cr(p_priv); +} /* butterfly_write_data */ + + +/* ************************************************************************* + * butterfly_close + * ************************************************************************* */ +static int butterfly_close(struct multiboot * p_mboot) +{ + bfly_privdata_t * p_priv = (bfly_privdata_t *)p_mboot->privdata; + + if (p_priv->progmode_active) + { + butterfly_leave_progmode(p_priv); + } + + butterfly_close_device(p_priv); + + return 0; +} /* butterfly_close */ + + +/* ************************************************************************* + * butterfly_open + * ************************************************************************* */ +static int butterfly_open(struct multiboot * p_mboot) +{ + bfly_privdata_t * p_priv = (bfly_privdata_t *)p_mboot->privdata; + + if (p_priv->p_device == NULL) + { + fprintf(stderr, "abort: no device given\n"); + return -1; + } + + if (butterfly_open_device(p_priv) < 0) + { + return -1; + } + + if (butterfly_enter_progmode(p_priv) != 0) + { + fprintf(stderr, "failed to enter progmode\n"); + butterfly_close_device(p_priv); + return -1; + } + + p_priv->progmode_active = 1; + + uint8_t signature[3]; + if (butterfly_get_signature(p_priv, signature) != 0) + { + fprintf(stderr, "failed to get signature\n"); + butterfly_close_device(p_priv); + return -1; + } + + const avr_chipinfo_t * p_chipinfo; + p_chipinfo = chipinfo_get_by_signature(signature); + if (p_chipinfo == NULL) + { + fprintf(stderr, "failed to identify chip signature [0x%02x 0x%02x 0x%02x]\n", + signature[0], signature[1], signature[2]); + butterfly_close_device(p_priv); + return -1; + } + + p_priv->flashsize = p_chipinfo->flashsize; + p_priv->eepromsize = p_chipinfo->eepromsize; + + if (butterfly_get_buffersize(p_priv, &p_priv->buffersize) != 0) + { + fprintf(stderr, "failed to get buffersize\n"); + butterfly_close_device(p_priv); + return -1; + } + + if (p_priv->twi_address != 0x00) + { + printf("twi address : 0x%02x\n", + p_priv->twi_address); + } + + printf("device : %-16s (sig: 0x%02x 0x%02x 0x%02x)\n", + p_chipinfo->name, p_chipinfo->sig[0], + p_chipinfo->sig[1], p_chipinfo->sig[2]); + + printf("flash size : 0x%04x / %5d\n", + p_chipinfo->flashsize, p_chipinfo->flashsize); + + printf("eeprom size : 0x%04x / %5d\n", + p_chipinfo->eepromsize, p_chipinfo->eepromsize); + + if (p_priv->chip_erase) + { + if (butterfly_chiperase(p_priv) != 0) + { + fprintf(stderr, "failed to chip erase\n"); + butterfly_close_device(p_priv); + return -1; + } + + printf("chip erased : OK\n"); + } + + return 0; +} /* butterfly_open */ + + +/* ************************************************************************* + * butterfly_read + * ************************************************************************* */ +static int butterfly_read(struct multiboot * p_mboot, + struct databuf * p_dbuf, + int memtype) +{ + bfly_privdata_t * p_priv = (bfly_privdata_t *)p_mboot->privdata; + char * p_progress_msg = (memtype == 'F') ? "reading flash" : "reading eeprom"; + + uint16_t pos = 0; + uint16_t size = (memtype == 'F') ? p_priv->flashsize : + p_priv->eepromsize; + + if (butterfly_set_address(p_priv, pos) < 0) + { + fprintf(stderr, "failed to set address\n"); + return -1; + } + + while (pos < size) + { + p_mboot->progress_cb(p_progress_msg, pos, size); + + uint16_t len = MIN(p_priv->buffersize, size - pos); + if (butterfly_read_data(p_priv, p_dbuf->data + pos, len, memtype)) + { + p_mboot->progress_cb(p_progress_msg, -1, -1); + return -1; + } + + pos += len; + } + + p_dbuf->length = pos; + + p_mboot->progress_cb(p_progress_msg, pos, size); + return 0; +} /* butterfly_read */ + + +/* ************************************************************************* + * butterfly_write + * ************************************************************************* */ +static int butterfly_write(struct multiboot * p_mboot, + struct databuf * p_dbuf, + int memtype) +{ + bfly_privdata_t * p_priv = (bfly_privdata_t *)p_mboot->privdata; + char * p_progress_msg = (memtype == 'F') ? "writing flash" : "writing eeprom"; + + uint16_t pos = 0; + + if (butterfly_set_address(p_priv, pos) < 0) + { + fprintf(stderr, "failed to set address\n"); + return -1; + } + + while (pos < p_dbuf->length) + { + p_mboot->progress_cb(p_progress_msg, pos, p_dbuf->length); + + uint16_t len = MIN(p_priv->buffersize, p_dbuf->length - pos); + if (butterfly_write_data(p_priv, p_dbuf->data + pos, len, memtype)) + { + p_mboot->progress_cb(p_progress_msg, -1, -1); + return -1; + } + + pos += len; + } + + p_mboot->progress_cb(p_progress_msg, pos, p_dbuf->length); + return 0; +} /* butterfly_write */ + + +/* ************************************************************************* + * butterfly_verify + * ************************************************************************* */ +static int butterfly_verify(struct multiboot * p_mboot, + struct databuf * p_dbuf, + int memtype) +{ + bfly_privdata_t * p_priv = (bfly_privdata_t *)p_mboot->privdata; + char * p_progress_msg = (memtype == 'F') ? "verifing flash" : "verifing eeprom"; + + uint16_t pos = 0; + uint8_t comp[256]; + + if (butterfly_set_address(p_priv, pos) < 0) + { + fprintf(stderr, "failed to set address\n"); + return -1; + } + + while (pos < p_dbuf->length) + { + p_mboot->progress_cb(p_progress_msg, pos, p_dbuf->length); + + uint16_t len = MIN(p_priv->buffersize, p_dbuf->length - pos); + if (butterfly_read_data(p_priv, comp, len, memtype)) + { + p_mboot->progress_cb(p_progress_msg, -1, -1); + return -1; + } + + if (memcmp(comp, p_dbuf->data + pos, len) != 0x00) + { + p_mboot->progress_cb(p_progress_msg, -1, -1); + fprintf(stderr, "verify failed at pos 0x%04x!!\n", pos); + return -1; + } + + pos += len; + } + + p_dbuf->length = pos; + + p_mboot->progress_cb(p_progress_msg, pos, p_dbuf->length); + return 0; +} /* butterfly_verify */ + + +struct multiboot_ops butterfly_ops = +{ + .exec_name = "butterfly_prog", + .alloc = butterfly_alloc, + .free = butterfly_free, + .get_memtype = butterfly_get_memtype, + .get_memsize = butterfly_get_memsize, + + .open = butterfly_open, + .close = butterfly_close, + .read = butterfly_read, + .write = butterfly_write, + .verify = butterfly_verify, +}; diff --git a/chipinfo_avr.c b/chipinfo_avr.c index 81a668c..47a9a88 100644 --- a/chipinfo_avr.c +++ b/chipinfo_avr.c @@ -27,35 +27,47 @@ #define ARRAY_SIZE(x) (sizeof(x) / sizeof(*x)) -struct chipinfo +static avr_chipinfo_t chips[] = { - uint8_t sig[3]; - const char name[16]; + { { 0x1E, 0x93, 0x07 }, "ATmega8", 0x2000, 0x200 }, + { { 0x1E, 0x93, 0x0A }, "ATmega88", 0x2000, 0x200 }, + { { 0x1E, 0x94, 0x06 }, "ATmega168", 0x4000, 0x200 }, + { { 0x1E, 0x95, 0x02 }, "ATmega32", 0x8000, 0x400 }, + { { 0x1E, 0x95, 0x0F }, "ATmega328p", 0x8000, 0x400 }, }; -static struct chipinfo chips[] = +/* ************************************************************************* + * chipinfo_get_by_signature + * ************************************************************************* */ +const avr_chipinfo_t * chipinfo_get_by_signature(const uint8_t *sig) { - { { 0x1E, 0x93, 0x07 }, "AVR Mega 8" }, - { { 0x1E, 0x93, 0x0A }, "AVR Mega 88" }, - { { 0x1E, 0x94, 0x06 }, "AVR Mega 168" }, - { { 0x1E, 0x95, 0x02 }, "AVR Mega 32" }, -}; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(chips); i++) + { + avr_chipinfo_t *chip = &chips[i]; + + if ((chip->sig[0] == sig[0]) && + (chip->sig[1] == sig[1]) && + (chip->sig[2] == sig[2]) + ) + { + return chip; + } + } + + return NULL; +} /* chipinfo_get_by_signature */ + /* ************************************************************************* * chipinfo_get_avr_name * ************************************************************************* */ const char * chipinfo_get_avr_name(const uint8_t *sig) { - unsigned int i; + const avr_chipinfo_t * p_chipinfo; - for (i = 0; i < ARRAY_SIZE(chips); i++) - { - struct chipinfo *chip = &chips[i]; - if (chip->sig[0] == sig[0] && chip->sig[1] == sig[1] && chip->sig[2] == sig[2]) - { - return chip->name; - } - } + p_chipinfo = chipinfo_get_by_signature(sig); - return "unknown"; + return (p_chipinfo != NULL) ? p_chipinfo->name : "unknown"; } /* chipinfo_get_avr_name */ diff --git a/chipinfo_avr.h b/chipinfo_avr.h index aebdda2..371826d 100644 --- a/chipinfo_avr.h +++ b/chipinfo_avr.h @@ -3,6 +3,15 @@ #include -const char * chipinfo_get_avr_name(const uint8_t *sig); +typedef struct avr_chipinfo_s +{ + uint8_t sig[3]; + const char name[16]; + uint16_t flashsize; + uint16_t eepromsize; +} avr_chipinfo_t; + +const avr_chipinfo_t * chipinfo_get_by_signature (const uint8_t *sig); +const char * chipinfo_get_avr_name (const uint8_t *sig); #endif /* _CHIPINFO_H_ */ diff --git a/multiboot.c b/multiboot.c index f78d383..bf628c9 100644 --- a/multiboot.c +++ b/multiboot.c @@ -39,6 +39,7 @@ static struct multiboot_ops * prog_ops[] = &mpm_ops, &funk_ops, &eprog_ops, + &butterfly_ops, }; struct mboot_action diff --git a/multiboot.h b/multiboot.h index 64477db..378923d 100644 --- a/multiboot.h +++ b/multiboot.h @@ -35,5 +35,6 @@ extern struct multiboot_ops twi_ops; extern struct multiboot_ops mpm_ops; extern struct multiboot_ops funk_ops; extern struct multiboot_ops eprog_ops; +extern struct multiboot_ops butterfly_ops; #endif /* _MULTIBOOT_H_ */