From ca2a0a99ae0e7d10ae8a6798481dec4a310081e6 Mon Sep 17 00:00:00 2001 From: Olaf Rempel Date: Sat, 11 Feb 2012 12:44:02 +0100 Subject: [PATCH] replace linux application --- linux/Makefile | 6 +- linux/chipinfo_avr.c | 52 +++ linux/chipinfo_avr.h | 8 + linux/filedata.c | 568 +++++++++++++------------- linux/filedata.h | 8 +- linux/mpm.c | 635 ++++++++++++++++++++++++++++++ linux/multiboot.c | 320 +++++++++++++++ linux/multiboot.h | 33 ++ linux/optarg.c | 193 +++++++++ linux/optarg.h | 15 + linux/rules/99-i2c-tiny-usb.rules | 2 + linux/twb.c | 329 ---------------- linux/twb.h | 31 -- linux/twi.c | 469 ++++++++++++++++++++++ linux/twiboot.c | 365 ----------------- 15 files changed, 2019 insertions(+), 1015 deletions(-) create mode 100644 linux/chipinfo_avr.c create mode 100644 linux/chipinfo_avr.h create mode 100644 linux/mpm.c create mode 100644 linux/multiboot.c create mode 100644 linux/multiboot.h create mode 100644 linux/optarg.c create mode 100644 linux/optarg.h create mode 100644 linux/rules/99-i2c-tiny-usb.rules delete mode 100644 linux/twb.c delete mode 100644 linux/twb.h create mode 100644 linux/twi.c delete mode 100644 linux/twiboot.c diff --git a/linux/Makefile b/linux/Makefile index a495786..718f5fa 100644 --- a/linux/Makefile +++ b/linux/Makefile @@ -1,4 +1,5 @@ -TARGET = twiboot +TARGET = twiboot +TARGET2 = mpmboot CFLAGS = -Wall -Wno-unused-result -O2 -MMD -MP -MF $(*F).d @@ -11,12 +12,13 @@ all: $(TARGET) $(TARGET): $(SRC:.c=.o) @echo " Linking file: $@" @$(CC) $(CFLAGS) $^ -o $@ $(LDFLAGS) > /dev/null + @ln -sf $@ $(TARGET2) %.o: %.c @echo " Building file: $<" @$(CC) -c $(CFLAGS) $< -o $@ clean: - rm -rf $(TARGET) *.o *.d + rm -rf $(TARGET) $(TARGET2) *.o *.d -include $(shell find . -name \*.d 2> /dev/null) diff --git a/linux/chipinfo_avr.c b/linux/chipinfo_avr.c new file mode 100644 index 0000000..8d84ace --- /dev/null +++ b/linux/chipinfo_avr.c @@ -0,0 +1,52 @@ +/*************************************************************************** +* Copyright (C) 10/2010 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 "chipinfo_avr.h" + +#define ARRAY_SIZE(x) (sizeof(x) / sizeof(*x)) + +struct chipinfo { + uint8_t sig[3]; + const char name[16]; +}; + +static struct chipinfo chips[] = { + { { 0x1E, 0x93, 0x07 }, "AVR Mega 8" }, + { { 0x1E, 0x93, 0x0A }, "AVR Mega 88" }, + { { 0x1E, 0x94, 0x06 }, "AVR Mega 168" }, + { { 0x1E, 0x95, 0x02 }, "AVR Mega 32" }, +}; + +const char * chipinfo_get_avr_name(const uint8_t *sig) +{ + int i; + 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; + } + + return "unknown"; +} diff --git a/linux/chipinfo_avr.h b/linux/chipinfo_avr.h new file mode 100644 index 0000000..aebdda2 --- /dev/null +++ b/linux/chipinfo_avr.h @@ -0,0 +1,8 @@ +#ifndef _CHIPINFO_H_ +#define _CHIPINFO_H_ + +#include + +const char * chipinfo_get_avr_name(const uint8_t *sig); + +#endif /* _CHIPINFO_H_ */ diff --git a/linux/filedata.c b/linux/filedata.c index 8c6543e..9d9de03 100644 --- a/linux/filedata.c +++ b/linux/filedata.c @@ -1,21 +1,21 @@ /*************************************************************************** - * Copyright (C) 10/2010 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. * - ***************************************************************************/ +* Copyright (C) 10/2010 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 @@ -31,393 +31,393 @@ #define FILETYPE_BINARY 1 #define FILETYPE_INTELHEX 2 -int dbuf_alloc(struct databuf **dbuf, uint32_t size) +struct databuf * dbuf_alloc(uint32_t size) { - *dbuf = malloc(sizeof(struct databuf) + size); - if (*dbuf == NULL) { - perror("dbuf_alloc"); - return -1; - } + struct databuf *dbuf = malloc(sizeof(struct databuf) + size); + if (dbuf == NULL) { + perror("dbuf_alloc"); + return NULL; + } - memset((*dbuf)->data, 0xFF, size); - (*dbuf)->size = size; - (*dbuf)->length = 0; - return 0; + memset(dbuf->data, 0xFF, size); + dbuf->size = size; + dbuf->length = 0; + return dbuf; } void dbuf_free(struct databuf *dbuf) { - free(dbuf); + free(dbuf); } static void dbuf_dump(struct databuf *dbuf) { - int pos = 0, oldskip = 0; + int pos = 0, oldskip = 0; - while (pos < dbuf->length) { - char buf[128]; - int j, i = 0; + while (pos < dbuf->length) { + char buf[128]; + int j, i = 0; - int skip = 1; - for (j = 0; j < 16; j++) { - if (pos + j < dbuf->length) - i += sprintf(buf + i, "%02X", dbuf->data[pos + j]); - else - i += sprintf(buf + i, " "); + int skip = 1; + for (j = 0; j < 16; j++) { + if (pos + j < dbuf->length) + i += sprintf(buf + i, "%02X", dbuf->data[pos + j]); + else + i += sprintf(buf + i, " "); - if (j % 2) - buf[i++] = ' '; - } + if (j % 2) + buf[i++] = ' '; + } - for (j = 0; j < 16; j++) { - if (pos + j < dbuf->length) { - unsigned char val = dbuf->data[pos + j]; - if (val >= 0x20 && val < 0x7F) - buf[i++] = val; - else - buf[i++] = '.'; + for (j = 0; j < 16; j++) { + if (pos + j < dbuf->length) { + unsigned char val = dbuf->data[pos + j]; + if (val >= 0x20 && val < 0x7F) + buf[i++] = val; + else + buf[i++] = '.'; - if (val != 0xFF) - skip = 0; - } else { - buf[i++] = ' '; - } - } + if (val != 0xFF) + skip = 0; + } else { + buf[i++] = ' '; + } + } - if (pos == 0 || (pos + 16) >= dbuf->length || skip == 0) { - buf[i++] = '\0'; - printf("%04X: %s\r\n", pos, buf); - oldskip = 0; + if (pos == 0 || (pos + 16) >= dbuf->length || skip == 0) { + buf[i++] = '\0'; + printf("%04X: %s\r\n", pos, buf); + oldskip = 0; - } else if (skip == 1 && oldskip == 0) { - printf("****\n"); - oldskip = 1; - } + } else if (skip == 1 && oldskip == 0) { + printf("****\n"); + oldskip = 1; + } - pos += 16; - } + pos += 16; + } } static int binfile_getsize(const char *filename, uint32_t *size) { - int fd = open(filename, O_RDONLY); - if (fd < 0) { - perror("binfile_getsize(): open()"); - return -1; - } + int fd = open(filename, O_RDONLY); + if (fd < 0) { + perror("binfile_getsize(): open()"); + return -1; + } - struct stat filestat; - if (fstat(fd, &filestat) < 0) { - perror("binfile_getsize(): fstat()"); - close(fd); - return -1; - } + struct stat filestat; + if (fstat(fd, &filestat) < 0) { + perror("binfile_getsize(): fstat()"); + close(fd); + return -1; + } - *size = filestat.st_size; + *size = filestat.st_size; - close(fd); - return 0; + close(fd); + return 0; } static int binfile_read(const char *filename, struct databuf *dbuf) { - int fd = open(filename, O_RDONLY); - if (fd < 0) { - perror("binfile_read(): open()"); - return -1; - } + int fd = open(filename, O_RDONLY); + if (fd < 0) { + perror("binfile_read(): open()"); + return -1; + } - ssize_t readsize = read(fd, dbuf->data, dbuf->size); - if (readsize <= 0) { - perror("binfile_read(): read()"); - close(fd); - return -1; - } + ssize_t readsize = read(fd, dbuf->data, dbuf->size); + if (readsize <= 0) { + perror("binfile_read(): read()"); + close(fd); + return -1; + } - dbuf->length = readsize; + dbuf->length = readsize; - close(fd); - return 0; + close(fd); + return 0; } static int binfile_write(const char *filename, struct databuf *dbuf) { - int fd = open(filename, O_RDWR | O_CREAT | O_TRUNC, 0644); - if (fd < 0) { - perror("binfile_write(): open()"); - return -1; - } + int fd = open(filename, O_RDWR | O_CREAT | O_TRUNC, 0644); + if (fd < 0) { + perror("binfile_write(): open()"); + return -1; + } - ssize_t writesize = write(fd, dbuf->data, dbuf->length); - if (writesize != dbuf->length) { - perror("binfile_write(): write()"); - close(fd); - return -1; - } + ssize_t writesize = write(fd, dbuf->data, dbuf->length); + if (writesize != dbuf->length) { + perror("binfile_write(): write()"); + close(fd); + return -1; + } - close(fd); - return 0; + close(fd); + return 0; } struct ihex_record { - uint8_t byte_count; - uint16_t address; - uint8_t type; + uint8_t byte_count; + uint16_t address; + uint8_t type; - uint8_t *data; - uint8_t chksum; + uint8_t *data; + uint8_t chksum; }; static uint8_t hex2byte(const char *ptr) { - int i; - uint8_t result = 0; + int i; + uint8_t result = 0; - for (i = 0; i < 2; i++) { - result <<= 4; - result |= (ptr[i] >= '0' && ptr[i] <= '9') ? (ptr[i] - '0') : - (((ptr[i] & 0xDF) >= 'A' && (ptr[i] & 0xDF) <= 'F') ? (ptr[i] - 'A' + 0x0A) : - 0x00); - } + for (i = 0; i < 2; i++) { + result <<= 4; + result |= (ptr[i] >= '0' && ptr[i] <= '9') ? (ptr[i] - '0') : + (((ptr[i] & 0xDF) >= 'A' && (ptr[i] & 0xDF) <= 'F') ? (ptr[i] - 'A' + 0x0A) : + 0x00); + } - return result; + return result; } static int hexfile_getrecord(FILE *stream, struct ihex_record *record) { - char *hexline = NULL; - size_t size; + char *hexline = NULL; + size_t size; - ssize_t length = getline(&hexline, &size, stream); - if (length == -1) { - if (!feof(stream)) { - perror("hexfile_getrecord(): getline()"); - } - return -1; - } + ssize_t length = getline(&hexline, &size, stream); + if (length == -1) { + if (!feof(stream)) { + perror("hexfile_getrecord(): getline()"); + } + return -1; + } - if (length < 12) { - fprintf(stderr, "record too short (%d)\n", length); - free(hexline); - return -1; - } + if (length < 12) { + fprintf(stderr, "record too short (%d)\n", length); + free(hexline); + return -1; + } - int pos = 0; - if (hexline[pos] != ':') { - fprintf(stderr, "invalid startcode\n"); - free(hexline); - return -1; - } - pos++; + int pos = 0; + if (hexline[pos] != ':') { + fprintf(stderr, "invalid startcode\n"); + free(hexline); + return -1; + } + pos++; - uint8_t chksum = 0x00; + uint8_t chksum = 0x00; - record->byte_count = hex2byte(&hexline[pos]); - chksum += record->byte_count; - pos += 2; + record->byte_count = hex2byte(&hexline[pos]); + chksum += record->byte_count; + pos += 2; - if (record->byte_count > 0) { - record->data = malloc(record->byte_count); - if (record->data == NULL) { - perror("hexfile_getrecord(): malloc()"); - free(hexline); - return -1; - } - } + if (record->byte_count > 0) { + record->data = malloc(record->byte_count); + if (record->data == NULL) { + perror("hexfile_getrecord(): malloc()"); + free(hexline); + return -1; + } + } - uint8_t hiaddr = hex2byte(&hexline[pos]); - uint8_t loaddr = hex2byte(&hexline[pos +2]); - record->address = (hiaddr << 8) + loaddr; - chksum += hiaddr + loaddr; - pos += 4; + uint8_t hiaddr = hex2byte(&hexline[pos]); + uint8_t loaddr = hex2byte(&hexline[pos +2]); + record->address = (hiaddr << 8) + loaddr; + chksum += hiaddr + loaddr; + pos += 4; - record->type = hex2byte(&hexline[pos]); - chksum += record->type; - pos += 2; + record->type = hex2byte(&hexline[pos]); + chksum += record->type; + pos += 2; - int i; - for (i = 0; i < record->byte_count; i++) { - record->data[i] = hex2byte(&hexline[pos]); - chksum += record->data[i]; - pos += 2; - } + int i; + for (i = 0; i < record->byte_count; i++) { + record->data[i] = hex2byte(&hexline[pos]); + chksum += record->data[i]; + pos += 2; + } - record->chksum = hex2byte(&hexline[pos]); - chksum += record->chksum; - pos += 2; + record->chksum = hex2byte(&hexline[pos]); + chksum += record->chksum; + pos += 2; - if (chksum != 0x00) { - fprintf(stderr, "invalid checksum (0x%02X)\n", chksum); - if (record->byte_count > 0) - free(record->data); - free(hexline); - return -1; - } + if (chksum != 0x00) { + fprintf(stderr, "invalid checksum (0x%02X)\n", chksum); + if (record->byte_count > 0) + free(record->data); + free(hexline); + return -1; + } - free(hexline); - return 0; + free(hexline); + return 0; } static int hexfile_putrecord(FILE *stream, struct ihex_record *record) { - uint8_t chksum = record->byte_count; - chksum += (record->address >> 8) & 0xFF; - chksum += (record->address & 0xFF); - chksum += record->type; + uint8_t chksum = record->byte_count; + chksum += (record->address >> 8) & 0xFF; + chksum += (record->address & 0xFF); + chksum += record->type; - int i, len = 0; - char buf[64]; + int i, len = 0; + char buf[64]; - buf[0] = '\0'; - for (i = 0; i < record->byte_count; i++) { - len += snprintf(buf + len, sizeof(buf) - len, "%02X", record->data[i]); - chksum += record->data[i]; - } + buf[0] = '\0'; + for (i = 0; i < record->byte_count; i++) { + len += snprintf(buf + len, sizeof(buf) - len, "%02X", record->data[i]); + chksum += record->data[i]; + } - fprintf(stream, ":%02X%04X%02X%s%02X\n", record->byte_count, record->address, record->type, buf, (uint8_t)(0x100 - chksum)); - return -1; + fprintf(stream, ":%02X%04X%02X%s%02X\n", record->byte_count, record->address, record->type, buf, (uint8_t)(0x100 - chksum)); + return -1; } static int hexfile_getsize(const char *filename, uint32_t *size) { - *size = 0x10000; - return 0; + *size = 0x10000; + return 0; } static int hexfile_read(const char *filename, struct databuf *dbuf) { - FILE *stream = fopen(filename, "r"); - if (stream == NULL) { - perror("hexfile_read(): fopen()"); - return -1; - } + FILE *stream = fopen(filename, "r"); + if (stream == NULL) { + perror("hexfile_read(): fopen()"); + return -1; + } - while (1) { - struct ihex_record record; - memset(&record, 0x00, sizeof(struct ihex_record)); + while (1) { + struct ihex_record record; + memset(&record, 0x00, sizeof(struct ihex_record)); - int result = hexfile_getrecord(stream, &record); - if (result == -1) - break; + int result = hexfile_getrecord(stream, &record); + if (result == -1) + break; - if (record.type == 0x00) { - if (record.address > dbuf->size || record.address + record.byte_count > dbuf->size) { - fprintf(stderr, "hexfile_read(): data out of bounds\n"); - break; - } + if (record.type == 0x00) { + if (record.address > dbuf->size || record.address + record.byte_count > dbuf->size) { + fprintf(stderr, "hexfile_read(): data out of bounds\n"); + break; + } - memcpy(&dbuf->data[record.address], record.data, record.byte_count); - dbuf->length = record.address + record.byte_count; - } - } + memcpy(&dbuf->data[record.address], record.data, record.byte_count); + dbuf->length = record.address + record.byte_count; + } + } - fclose(stream); - return 0; + fclose(stream); + return 0; } static int hexfile_write(const char *filename, struct databuf *dbuf) { - FILE *stream = fopen(filename, "w"); - if (stream == NULL) { - perror("hexfile_write(): fopen()"); - return -1; - } + FILE *stream = fopen(filename, "w"); + if (stream == NULL) { + perror("hexfile_write(): fopen()"); + return -1; + } - int i; - int addr_min = dbuf->length; - int addr_max = 0; - for (i = 0; i < dbuf->length; i++) { - if (dbuf->data[i] == 0xFF) - continue; + int i; + int addr_min = dbuf->length; + int addr_max = 0; + for (i = 0; i < dbuf->length; i++) { + if (dbuf->data[i] == 0xFF) + continue; - if (addr_min > i) - addr_min = i; + if (addr_min > i) + addr_min = i; - if (addr_max < i) - addr_max = i; - } - addr_min = addr_min & ~0x0F; - addr_max = (addr_max + 0x0F) & ~0x0F; + if (addr_max < i) + addr_max = i; + } + addr_min = addr_min & ~0x0F; + addr_max = (addr_max + 0x0F) & ~0x0F; - struct ihex_record record; - for (i = addr_min; i < addr_max; i += 0x10) { - record.byte_count = 0x10; - record.address = i; - record.type = 0x00; - record.data = &dbuf->data[i]; + struct ihex_record record; + for (i = addr_min; i < addr_max; i += 0x10) { + record.byte_count = 0x10; + record.address = i; + record.type = 0x00; + record.data = &dbuf->data[i]; - hexfile_putrecord(stream, &record); - } + hexfile_putrecord(stream, &record); + } - record.byte_count = 0x00; - record.address = addr_min; - record.type = 0x01; - record.data = NULL; - hexfile_putrecord(stream, &record); + record.byte_count = 0x00; + record.address = addr_min; + record.type = 0x01; + record.data = NULL; + hexfile_putrecord(stream, &record); - fclose(stream); - return 0; + fclose(stream); + return 0; } static int get_filetype(const char *filename) { - const char *ext = filename + (strlen(filename) -4); + const char *ext = filename + (strlen(filename) -4); - if (ext < filename) - return FILETYPE_UNKNOWN; + if (ext < filename) + return FILETYPE_UNKNOWN; - if (strncmp(ext, ".bin", 4) == 0) - return FILETYPE_BINARY; + if (strncmp(ext, ".bin", 4) == 0) + return FILETYPE_BINARY; - if (strncmp(ext, ".hex", 4) == 0) - return FILETYPE_INTELHEX; + if (strncmp(ext, ".hex", 4) == 0) + return FILETYPE_INTELHEX; - return FILETYPE_UNKNOWN; + return FILETYPE_UNKNOWN; } int file_getsize(const char *filename, uint32_t *size) { - switch (get_filetype(filename)) { - case FILETYPE_BINARY: - return binfile_getsize(filename, size); + switch (get_filetype(filename)) { + case FILETYPE_BINARY: + return binfile_getsize(filename, size); - case FILETYPE_INTELHEX: - return hexfile_getsize(filename, size); + case FILETYPE_INTELHEX: + return hexfile_getsize(filename, size); - default: - return -1; - } + default: + return -1; + } } int file_read(const char *filename, struct databuf *dbuf) { - switch (get_filetype(filename)) { - case FILETYPE_BINARY: - return binfile_read(filename, dbuf); + switch (get_filetype(filename)) { + case FILETYPE_BINARY: + return binfile_read(filename, dbuf); - case FILETYPE_INTELHEX: - return hexfile_read(filename, dbuf); + case FILETYPE_INTELHEX: + return hexfile_read(filename, dbuf); - default: - return -1; - } + default: + return -1; + } } int file_write(const char *filename, struct databuf *dbuf) { - if (strncmp(filename, "-", 1) == 0) { - dbuf_dump(dbuf); - return 0; - } + if (strncmp(filename, "-", 1) == 0) { + dbuf_dump(dbuf); + return 0; + } - switch (get_filetype(filename)) { - case FILETYPE_BINARY: - return binfile_write(filename, dbuf); + switch (get_filetype(filename)) { + case FILETYPE_BINARY: + return binfile_write(filename, dbuf); - case FILETYPE_INTELHEX: - return hexfile_write(filename, dbuf); + case FILETYPE_INTELHEX: + return hexfile_write(filename, dbuf); - default: - return -1; - } + default: + return -1; + } } diff --git a/linux/filedata.h b/linux/filedata.h index f11fef1..4a32ee1 100644 --- a/linux/filedata.h +++ b/linux/filedata.h @@ -4,12 +4,12 @@ #include struct databuf { - uint32_t size; // allocation size - uint32_t length; // used size - uint8_t data[0]; + uint32_t size; // allocation size + uint32_t length; // used size + uint8_t data[0]; }; -int dbuf_alloc(struct databuf **dbuf, uint32_t size); +struct databuf * dbuf_alloc(uint32_t size); void dbuf_free(struct databuf *dbuf); int file_getsize(const char *filename, uint32_t *size); diff --git a/linux/mpm.c b/linux/mpm.c new file mode 100644 index 0000000..e71a003 --- /dev/null +++ b/linux/mpm.c @@ -0,0 +1,635 @@ +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "chipinfo_avr.h" +#include "multiboot.h" +#include "optarg.h" + +#define READ_BLOCK_SIZE 256 /* bytes in one flash/eeprom read request */ +#define WRITE_BLOCK_SIZE 16 /* bytes in one eeprom write request */ + +#define CMD_SWITCH_APPLICATION 0x01 +#define CMD_GET_BOOTLOADER_VERSION 0x02 +#define CMD_GET_CHIP_INFO 0x03 +#define CMD_READ_MEMORY 0x11 +#define CMD_WRITE_MEMORY 0x12 + +#define CAUSE_SUCCESS 0x00 +#define CAUSE_COMMAND_NOT_SUPPORTED 0xF0 +#define CAUSE_INVALID_PARAMETER 0xF1 +#define CAUSE_UNSPECIFIED_ERROR 0xFF + +/* CMD_SWITCH_APPLICATION parameter */ +#define BOOTTYPE_BOOTLOADER 0x00 +#define BOOTTYPE_APPLICATION 0x80 + +#define MEMTYPE_FLASH 0x01 +#define MEMTYPE_EEPROM 0x02 + +#define ARRAY_SIZE(x) (sizeof(x) / sizeof(*x)) +#define MIN(a, b) ((a) < (b) ? (a) : (b)) + +struct multiboot_ops mpm_ops; + +struct mpm_privdata { + char *device; + int fd; + int connected; + + int address; + + int flashsize; + int flashpage; + int eepromsize; + + struct termios oldtio; +}; + +static struct option mpm_optargs[] = { + {"address", 1, 0, 'a'}, /* -a */ + {"device", 1, 0, 'd'}, /* [ -d ] */ +}; + +static int mpm_optarg_cb(int val, const char *arg, void *privdata) +{ + struct mpm_privdata *mpm = (struct mpm_privdata *)privdata; + + switch (val) { + case 'a': /* address */ + { + char *endptr; + mpm->address = strtol(arg, &endptr, 16); + if (*endptr != '\0' || mpm->address < 0x01 || mpm->address > 0x7F) { + fprintf(stderr, "invalid address: '%s'\n", arg); + return -1; + } + } + break; + + case 'd': /* device */ + { + if (mpm->device != NULL) { + fprintf(stderr, "invalid device: '%s'\n", optarg); + return -1; + } + + mpm->device = strdup(optarg); + if (mpm->device == NULL) { + perror("strdup()"); + return -1; + } + } + break; + + case 'h': + case '?': /* error */ + fprintf(stderr, "Usage: mpmboot [options]\n" + " -a
- selects mpm address (0x01 - 0xFF)\n" + " -d - selects mpm 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: mpmboot -d /dev/ttyUSB0 -a 0x22 -w flash:blmc.hex -w flash:blmc_eeprom.hex\n" + "\n"); + return -1; + + default: + return 1; + } + + return 0; +} + +static struct multiboot * mpm_alloc(void) +{ + struct multiboot * mboot = malloc(sizeof(struct multiboot)); + if (mboot == NULL) + return NULL; + + memset(mboot, 0x00, sizeof(struct multiboot)); + mboot->ops = &mpm_ops; + + struct mpm_privdata *mpm = malloc(sizeof(struct mpm_privdata)); + if (mpm == NULL) { + free(mboot); + return NULL; + } + + memset(mpm, 0x00, sizeof(struct mpm_privdata)); + mpm->device = NULL; + mpm->address = 0; + + optarg_register(mpm_optargs, ARRAY_SIZE(mpm_optargs), mpm_optarg_cb, (void *)mpm); + + mboot->privdata = mpm; + return mboot; +} + +static void mpm_free(struct multiboot *mboot) +{ + struct mpm_privdata *mpm = (struct mpm_privdata *)mboot->privdata; + + if (mpm->device != NULL) + free(mpm->device); + + free(mpm); + free(mboot); +} + +static int mpm_get_memtype(struct multiboot *mboot, const char *memname) +{ + if (strcmp(memname, "flash") == 0) + return MEMTYPE_FLASH; + + else if (strcmp(memname, "eeprom") == 0) + return MEMTYPE_EEPROM; + + return -1; +} + +static int mpm_get_memsize(struct multiboot *mboot, int memtype) +{ + struct mpm_privdata *mpm = (struct mpm_privdata *)mboot->privdata; + + if (!mpm->connected) + return 0; + + switch (memtype) { + case MEMTYPE_FLASH: + return mpm->flashsize; + + case MEMTYPE_EEPROM: + return mpm->eepromsize; + + default: + return 0; + } +} + +static int mpm_send(struct mpm_privdata *mpm, uint8_t command, uint8_t *data, int length) +{ + struct termios tio; + + if (tcgetattr(mpm->fd, &tio) < 0) { + perror("tcgetattr(tio)"); + return -1; + } + + tio.c_cflag |= PARODD; + + if (tcsetattr(mpm->fd, TCSAFLUSH, &tio) < 0) { + perror("tcsetattr(tio)"); + return -1; + } + +// usleep(5000); + + uint8_t address = mpm->address; + if (write(mpm->fd, &address, sizeof(address)) != sizeof(address)) { + perror("write(address)"); + return -1; + } + + usleep(500); + + tio.c_cflag &= ~(PARODD); + + if (tcsetattr(mpm->fd, TCSAFLUSH, &tio) < 0) { + perror("tcsetattr(tio)"); + return -1; + } + + uint8_t header[3]; + header[0] = command; + header[1] = (length >> 8) & 0xFF; + header[2] = length & 0xFF; + + if (write(mpm->fd, header, sizeof(header)) != sizeof(header)) { + perror("write(header)"); + return -1; + } + + if (data != NULL && length != 0) { + if (write(mpm->fd, data, length) != length) { + perror("write(data)"); + return -1; + } + } + + return 0; +} + +static int myread(int fd, void *data, int size) +{ + int pos = 0; + + while (1) { + fd_set fdset; + struct timeval timeout = { .tv_sec = 1, .tv_usec = 0 }; + + FD_ZERO(&fdset); + FD_SET(fd, &fdset); + + int ret = select(fd +1, &fdset, NULL, NULL, &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; +} + +static int mpm_recv(struct mpm_privdata *mpm, uint8_t command, uint8_t *cause, uint8_t *buffer, uint16_t buffersize) +{ + int len; + uint8_t header[4]; + + len = myread(mpm->fd, header, sizeof(header)); + if (len != sizeof(header)) { + fprintf(stderr, "short read() from device (not addressed?)\n"); + return -1; + } + + if (header[0] != command) { + fprintf(stderr, "invalid command response (0x%02x != 0x%02x)\n", header[0], command); + return -1; + } + + *cause = header[1]; + + uint16_t length = (header[2] << 8) | header[3]; +// printf("mpm_recv() cmd=0x%02x cause=0x%02x length=0x%04x\n", command, *cause, length); + + uint16_t bufferpos = 0; + while (length > 0) { + + /* free space in output buffer? */ + if ((bufferpos < buffersize) && (buffer != NULL)) { + + uint16_t size = MIN(buffersize - bufferpos, length); + len = myread(mpm->fd, buffer + bufferpos, size); + if (len <= 0) { + fprintf(stderr, "short read() from device (%d != %d)\n", len, size); + return -1; + } + bufferpos += len; + length -= len; + + } else { + uint8_t dummy[256]; + + /* no space in output buffer, but device still sends data -> do dummy read */ + uint16_t size = MIN(sizeof(dummy), length); + len = myread(mpm->fd, dummy, size); + if (len <= 0) { + fprintf(stderr, "short read() from device (%d != %d)\n", len, size); + return -1; + } + length -= len; + } + } + + return bufferpos; +} + +static void mpm_close_device(struct mpm_privdata *mpm) +{ + /* delay close() / tcsetattr() */ + usleep(100000); + + tcsetattr(mpm->fd, TCSANOW, &mpm->oldtio); + close(mpm->fd); +} + +static int mpm_open_device(struct mpm_privdata *mpm) +{ + mpm->fd = open(mpm->device, O_RDWR | O_NOCTTY | O_CLOEXEC); + if (mpm->fd < 0) { + perror("open()"); + return -1; + } + + if (tcgetattr(mpm->fd, &mpm->oldtio) < 0) { + perror("tcgetattr(oldtio)"); + close(mpm->fd); + return -1; + } + + struct termios newtio; + memset(&newtio, 0, sizeof(newtio)); + + newtio.c_iflag |= IGNBRK; + newtio.c_cflag |= B115200 | CS8 | CLOCAL | CREAD | PARENB | CMSPAR; + + newtio.c_cc[VMIN] = 1; + newtio.c_cc[VTIME] = 0; + + int err = tcsetattr(mpm->fd, TCSANOW, &newtio); + if (err < 0) { + perror("tcsetattr(newtio)"); + close(mpm->fd); + return -1; + } + + mpm->connected = 1; + return 0; +} + +static int mpm_switch_application(struct mpm_privdata *mpm, uint8_t application) +{ + uint8_t data[] = { application }; + int ret = mpm_send(mpm, CMD_SWITCH_APPLICATION, data, sizeof(data)); + if (ret < 0) + return ret; + + uint8_t cause = CAUSE_SUCCESS; + ret = mpm_recv(mpm, CMD_SWITCH_APPLICATION, &cause, NULL, 0); + if (ret < 0) + return ret; + + return (cause != CAUSE_SUCCESS); +} + +static int mpm_read_version(struct mpm_privdata *mpm, uint8_t *version, uint16_t length) +{ + memset(version, 0, length); + + int ret = mpm_send(mpm, CMD_GET_BOOTLOADER_VERSION, NULL, 0); + if (ret < 0) + return ret; + + uint8_t cause = CAUSE_SUCCESS; + ret = mpm_recv(mpm, CMD_GET_BOOTLOADER_VERSION, &cause, version, length); + if (ret < 0) + return ret; + + int i; + for (i = 0; i < length; i++) + version[i] &= ~0x80; + + return (cause != CAUSE_SUCCESS); +} + +static int mpm_read_chipinfo(struct mpm_privdata *mpm, uint8_t *chipinfo, uint16_t length) +{ + int ret = mpm_send(mpm, CMD_GET_CHIP_INFO, NULL, 0); + if (ret < 0) + return ret; + + uint8_t cause = CAUSE_SUCCESS; + ret = mpm_recv(mpm, CMD_GET_CHIP_INFO, &cause, chipinfo, length); + if (ret < 0) + return ret; + + return (cause != CAUSE_SUCCESS); +} + +static int mpm_read_memory(struct mpm_privdata *mpm, uint8_t *buffer, uint16_t size, uint8_t memtype, uint16_t address) +{ + uint8_t param[5] = { + memtype, + (address >> 8) & 0xFF, + (address & 0xFF), + (size >> 8) & 0xFF, + (size & 0xFF) + }; + + int ret = mpm_send(mpm, CMD_READ_MEMORY, param, sizeof(param)); + if (ret < 0) + return ret; + + uint8_t cause = CAUSE_SUCCESS; + ret = mpm_recv(mpm, CMD_READ_MEMORY, &cause, buffer, size); + if (ret < 0) + return ret; + + return (cause != CAUSE_SUCCESS); +} + +static int mpm_write_memory(struct mpm_privdata *mpm, uint8_t *buffer, uint16_t size, uint8_t memtype, uint16_t address) +{ + int bufsize; + if (memtype == MEMTYPE_FLASH) { + if ((address & (mpm->flashpage -1)) != 0x00) { + fprintf(stderr, "mpm_write_memory(): address 0x%04x not aligned to pagesize 0x%02x\n", address, mpm->flashpage); + return -1; + } + bufsize = 5 + mpm->flashpage; + + } else { + bufsize = 5 + size; + } + + uint8_t *cmd = malloc(bufsize); + if (cmd == NULL) + return -1; + + cmd[0] = memtype; + cmd[1] = (address >> 8) & 0xFF; + cmd[2] = (address & 0xFF); + cmd[3] = ((bufsize -5) >> 8) & 0xFF; + cmd[4] = ((bufsize -5) & 0xFF); + memcpy(cmd +5, buffer, size); + + if (memtype == MEMTYPE_FLASH) { + memset(cmd +5 +size, 0xFF, mpm->flashpage - size); + } + + int ret = mpm_send(mpm, CMD_WRITE_MEMORY, cmd, bufsize); + if (ret < 0) + return ret; + + free(cmd); + + uint8_t cause = CAUSE_SUCCESS; + ret = mpm_recv(mpm, CMD_WRITE_MEMORY, &cause, NULL, 0); + if (ret < 0) + return ret; + + return (cause != CAUSE_SUCCESS); +} + +static int mpm_close(struct multiboot *mboot) +{ + struct mpm_privdata *mpm = (struct mpm_privdata *)mboot->privdata; + + if (mpm->connected) + mpm_switch_application(mpm, BOOTTYPE_APPLICATION); + + mpm_close_device(mpm); + return 0; +} + +static int mpm_open(struct multiboot *mboot) +{ + struct mpm_privdata *mpm = (struct mpm_privdata *)mboot->privdata; + + if (mpm->address == 0) { + fprintf(stderr, "abort: no address given\n"); + return -1; + } + + if (mpm->device == NULL) { + fprintf(stderr, "abort: no device given\n"); + return -1; + } + + if (mpm_open_device(mpm) < 0) + return -1; + + if (mpm_switch_application(mpm, BOOTTYPE_BOOTLOADER)) { + fprintf(stderr, "failed to switch to bootloader (invalid address?)\n"); + mpm_close(mboot); + return -1; + } + + /* wait for watchdog and startup time */ + usleep(100000); + + char version[16]; + if (mpm_read_version(mpm, (uint8_t *)version, sizeof(version))) { + fprintf(stderr, "failed to get bootloader version"); + mpm_close(mboot); + return -1; + } + + uint8_t chipinfo[8]; + if (mpm_read_chipinfo(mpm, chipinfo, sizeof(chipinfo))) { + fprintf(stderr, "failed to get bootloader version"); + mpm_close(mboot); + return -1; + } + + const char *chipname = chipinfo_get_avr_name(chipinfo); + + mpm->flashpage = chipinfo[3]; + mpm->flashsize = (chipinfo[4] << 8) + chipinfo[5]; + mpm->eepromsize = (chipinfo[6] << 8) + chipinfo[7]; + + printf("device : %-16s (address: 0x%02X)\n", mpm->device, mpm->address); + printf("version : %-16s (sig: 0x%02x 0x%02x 0x%02x => %s)\n", version, chipinfo[0], chipinfo[1], chipinfo[2], chipname); + printf("flash size : 0x%04x / %5d (0x%02x bytes/page)\n", mpm->flashsize, mpm->flashsize, mpm->flashpage); + printf("eeprom size : 0x%04x / %5d\n", mpm->eepromsize, mpm->eepromsize); + + return 0; +} + +static int mpm_read(struct multiboot *mboot, struct databuf *dbuf, int memtype) +{ + struct mpm_privdata *mpm = (struct mpm_privdata *)mboot->privdata; + char *progress_msg = (memtype == MEMTYPE_FLASH) ? "reading flash" : "reading eeprom"; + + int pos = 0; + int size = (memtype == MEMTYPE_FLASH) ? mpm->flashsize : mpm->eepromsize; + while (pos < size) { + mboot->progress_cb(progress_msg, pos, size); + + int len = MIN(READ_BLOCK_SIZE, size - pos); + if (mpm_read_memory(mpm, dbuf->data + pos, len, memtype, pos)) { + mboot->progress_cb(progress_msg, -1, -1); + return -1; + } + + pos += len; + } + + dbuf->length = pos; + + mboot->progress_cb(progress_msg, pos, size); + return 0; +} + +static int mpm_write(struct multiboot *mboot, struct databuf *dbuf, int memtype) +{ + struct mpm_privdata *mpm = (struct mpm_privdata *)mboot->privdata; + char *progress_msg = (memtype == MEMTYPE_FLASH) ? "writing flash" : "writing eeprom"; + + int pos = 0; + while (pos < dbuf->length) { + mboot->progress_cb(progress_msg, pos, dbuf->length); + + int len = (memtype == MEMTYPE_FLASH) ? mpm->flashpage : WRITE_BLOCK_SIZE; + + len = MIN(len, dbuf->length - pos); + if (mpm_write_memory(mpm, dbuf->data + pos, len, memtype, pos)) { + mboot->progress_cb(progress_msg, -1, -1); + return -1; + } + + pos += len; + } + + mboot->progress_cb(progress_msg, pos, dbuf->length); + return 0; +} + +static int mpm_verify(struct multiboot *mboot, struct databuf *dbuf, int memtype) +{ + struct mpm_privdata *mpm = (struct mpm_privdata *)mboot->privdata; + char *progress_msg = (memtype == MEMTYPE_FLASH) ? "verifing flash" : "verifing eeprom"; + + int pos = 0; + uint8_t comp[READ_BLOCK_SIZE]; + while (pos < dbuf->length) { + mboot->progress_cb(progress_msg, pos, dbuf->length); + + int len = MIN(READ_BLOCK_SIZE, dbuf->length - pos); + if (mpm_read_memory(mpm, comp, len, memtype, pos)) { + mboot->progress_cb(progress_msg, -1, -1); + return -1; + } + + if (memcmp(comp, dbuf->data + pos, len) != 0x00) { + mboot->progress_cb(progress_msg, -1, -1); + fprintf(stderr, "verify failed at page 0x%04x!!\n", pos); + return -1; + } + + pos += len; + } + + dbuf->length = pos; + + mboot->progress_cb(progress_msg, pos, dbuf->length); + return 0; +} + +struct multiboot_ops mpm_ops = { + .alloc = mpm_alloc, + .free = mpm_free, + .get_memtype = mpm_get_memtype, + .get_memsize = mpm_get_memsize, + + .open = mpm_open, + .close = mpm_close, + .read = mpm_read, + .write = mpm_write, + .verify = mpm_verify, +}; diff --git a/linux/multiboot.c b/linux/multiboot.c new file mode 100644 index 0000000..3fb88b9 --- /dev/null +++ b/linux/multiboot.c @@ -0,0 +1,320 @@ +/*************************************************************************** +* Copyright (C) 10/2010 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 "filedata.h" +#include "list.h" +#include "multiboot.h" +#include "optarg.h" + +#define ARRAY_SIZE(x) (sizeof(x) / sizeof(*x)) + +#define ACTION_READ 0x01 +#define ACTION_WRITE 0x02 + +struct mboot_action { + struct list_head list; + + char *filename; + int memtype; + int mode; +}; + +static LIST_HEAD(action_list); + +static struct option main_optargs[] = { + {"help", 0, 0, 'h'}, /* [ -h ] */ + {"progress", 1, 0, 'p'}, /* [ -p <0|1|2> ] */ + {"read", 1, 0, 'r'}, /* [ -r : ] */ + {"write", 1, 0, 'w'}, /* [ -w : ] */ + {"no-verify", 0, 0, 'n'}, /* [ -n ] */ + {0, 0, 0, 0} +}; + +static void progress_mode0_cb(const char *msg, int pos, int size) +{ + /* no progress output */ +} + +static void progress_mode1_cb(const char *msg, int pos, int size) +{ + if (pos != -1 && size != -1) { + char stars[50]; + + int i; + int count = (pos * sizeof(stars) / size); + for (i = 0; i < sizeof(stars); i++) + stars[i] = (i < count) ? '*' : ' '; + + printf("%-15s: [%s] (%d)\r", msg, stars, pos); + } + + if (pos == size) + printf("\n"); +} + +static void progress_mode2_cb(const char *msg, int pos, int size) +{ + static int old_count; + + if (pos != -1 && size != -1) { + if (pos == 0) { + old_count = 0; + printf("%-15s: [", msg); + + } else if (pos <=size) { + int i; + int count = (pos * 50 / size); + for (i = old_count; i < count; i++) + printf("*"); + + old_count = count; + + if (pos == size) { + printf("] (%d)\n", pos); + } + } + } +} + +static int add_action(struct multiboot *mboot, int mode, const char *arg) +{ + struct mboot_action *action = malloc(sizeof(struct mboot_action)); + if (action == NULL) { + perror("malloc()"); + return -1; + } + + char *argcopy = strdup(arg); + if (argcopy == NULL) { + perror("strdup()"); + free(action); + return -1; + } + + char *tok = strtok(argcopy, ":"); + if (tok == NULL) { + fprintf(stderr, "invalid argument: '%s'\n", arg); + free(argcopy); + free(action); + return -1; + } + + action->memtype = mboot->ops->get_memtype(mboot, tok); + if (action->memtype == -1) { + fprintf(stderr, "invalid memtype: '%s'\n", tok); + free(argcopy); + free(action); + return -1; + } + + tok = strtok(NULL, ":"); + if (tok == NULL) { + fprintf(stderr, "invalid argument: '%s'\n", arg); + free(argcopy); + free(action); + return -1; + } + + action->filename = strdup(tok); + if (action->filename == NULL) { + perror("strdup()"); + free(argcopy); + free(action); + return -1; + } + + action->mode = mode; + + list_add_tail(&action->list, &action_list); + free(argcopy); + return 0; +} + +static int main_optarg_cb(int val, const char *arg, void *privdata) +{ + struct multiboot *mboot = (struct multiboot *)privdata; + + switch (val) { + case 'r': /* read */ + { + if (add_action(mboot, ACTION_READ, arg) < 0) + return -1; + } + break; + + case 'w': /* write */ + { + if (add_action(mboot, ACTION_WRITE, arg) < 0) + return -1; + } + break; + + case 'n': /* no verify after write */ + mboot->verify = 0; + break; + + case 'p': + { + switch (*arg) { + case '0': + mboot->progress_cb = progress_mode0_cb; + break; + + case '1': + mboot->progress_cb = progress_mode1_cb; + break; + + case '2': + mboot->progress_cb = progress_mode2_cb; + break; + + default: + fprintf(stderr, "invalid progress bar mode: '%s'\n", arg); + return -1; + } + } + break; + } + + return 0; +} + +int main(int argc, char *argv[]) +{ + struct multiboot *mboot; + + char *progname = strrchr(argv[0], '/'); + progname = (progname != NULL) ? (progname +1) : argv[0]; + + if (strcmp(progname, "twiboot") == 0) { + mboot = twi_ops.alloc(); + } else if (strcmp(progname, "mpmboot") == 0) { + mboot = mpm_ops.alloc(); + } else { + fprintf(stderr, "invalid progname, use 'twiboot' or 'mpmboot'\n"); + return -1; + } + + if (mboot == NULL) + return -1; + + mboot->verify = 1; + mboot->progress_cb = progress_mode1_cb; + + optarg_register(main_optargs, ARRAY_SIZE(main_optargs), main_optarg_cb, (void *)mboot); + int abort = optarg_parse(argc, argv); + + if (abort == -1 || mboot->ops->open(mboot) != 0) + return -1; + + setbuf(stdout, NULL); + + struct mboot_action *action, *tmp; + list_for_each_entry(action, &action_list, list) { + abort = 1; + if (action->mode == ACTION_READ) { + int memsize = mboot->ops->get_memsize(mboot, action->memtype); + if (memsize == 0) + break; + + struct databuf *dbuf = dbuf_alloc(memsize); + if (dbuf == NULL) + break; + + int result = mboot->ops->read(mboot, dbuf, action->memtype); + if (result != 0) { + fprintf(stderr, "failed to read from device\n"); + dbuf_free(dbuf); + break; + } + + result = file_write(action->filename, dbuf); + if (result != 0) { + fprintf(stderr, "failed to write file '%s'\n", action->filename); + dbuf_free(dbuf); + break; + } + + dbuf_free(dbuf); + abort = 0; + + } else if (action->mode == ACTION_WRITE) { + unsigned int size; + int result; + + result = file_getsize(action->filename, &size); + if (result != 0) + break; + + struct databuf *dbuf = dbuf_alloc(size); + if (dbuf == NULL) + break; + + result = file_read(action->filename, dbuf); + if (result != 0) { + fprintf(stderr, "failed to read file '%s'\n", action->filename); + dbuf_free(dbuf); + break; + } + + int memsize = mboot->ops->get_memsize(mboot, action->memtype); + if (memsize == 0) { + fprintf(stderr, "invalid memsize: 0x%04x > 0x%04x\n", dbuf->length, memsize); + dbuf_free(dbuf); + break; + } + + result = mboot->ops->write(mboot, dbuf, action->memtype); + if (result != 0) { + fprintf(stderr, "failed to write to device\n"); + dbuf_free(dbuf); + break; + } + + if (mboot->verify) { + result = mboot->ops->verify(mboot, dbuf, action->memtype); + if (result != 0) { + fprintf(stderr, "failed to verify\n"); + dbuf_free(dbuf); + break; + } + } + + dbuf_free(dbuf); + abort = 0; + } + } + + list_for_each_entry_safe(action, tmp, &action_list, list) { + free(action->filename); + free(action); + } + + mboot->ops->close(mboot); + mboot->ops->free(mboot); + + optarg_free(); + return abort; +} diff --git a/linux/multiboot.h b/linux/multiboot.h new file mode 100644 index 0000000..fea08e7 --- /dev/null +++ b/linux/multiboot.h @@ -0,0 +1,33 @@ +#ifndef _MULTIBOOT_H_ +#define _MULTIBOOT_H_ + +#include +#include "filedata.h" + +struct multiboot { + struct multiboot_ops *ops; + void *privdata; + + int verify; + void (* progress_cb)(const char *msg, int pos, int max); +}; + +struct multiboot_ops { + struct multiboot * (* alloc)(void); + void (* free)(struct multiboot *mboot); + + int (* get_memtype)(struct multiboot *mboot, const char *memname); + int (* get_memsize)(struct multiboot *mboot, int memtype); + + int (* open)(struct multiboot *mboot); + int (* close)(struct multiboot *mboot); + + int (* read)(struct multiboot *mboot, struct databuf *dbuf, int memtype); + int (* verify)(struct multiboot *mboot, struct databuf *dbuf, int memtype); + int (* write)(struct multiboot *mboot, struct databuf *dbuf, int memtype); +}; + +extern struct multiboot_ops twi_ops; +extern struct multiboot_ops mpm_ops; + +#endif /* _MULTIBOOT_H_ */ diff --git a/linux/optarg.c b/linux/optarg.c new file mode 100644 index 0000000..c452b98 --- /dev/null +++ b/linux/optarg.c @@ -0,0 +1,193 @@ +/*************************************************************************** +* Copyright (C) 10/2010 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 "list.h" +#include "optarg.h" + +#define ARRAY_SIZE(x) (sizeof(x) / sizeof(*x)) + +struct optarg_entry { + struct list_head list; + const struct option *opts; + int count; + + int (* parser_cb)(int val, const char *arg, void *privdata); + void *privdata; +}; + +static LIST_HEAD(option_list); + +int optarg_register(const struct option *opts, int count, + int (* parser_cb)(int val, const char *arg, void *privdata), + void *privdata) +{ + struct optarg_entry *entry; + + entry = malloc(sizeof(struct optarg_entry)); + if (entry == NULL) + return -1; + + entry->opts = opts; /* TODO: copy? */ + entry->count = count; + entry->parser_cb = parser_cb; + entry->privdata = privdata; + + list_add_tail(&entry->list, &option_list); + return 0; +} + +void optarg_free(void) +{ + struct optarg_entry *entry, *entry_tmp; + + list_for_each_entry_safe(entry, entry_tmp, &option_list, list) { + list_del(&entry->list); + free(entry); + } +} + +static void optarg_getsize(int *opt_count, int *optstring_len) +{ + int count = 0; + int length = 0; + + struct optarg_entry *entry; + + list_for_each_entry(entry, &option_list, list) { + count += entry->count; + + int i; + for (i = 0; i < entry->count; i++) { + switch (entry->opts[i].has_arg) { + case 0: /* no arguments */ + case 1: /* has argument */ + case 2: /* optional argument */ + length += entry->opts[i].has_arg +1; + break; + default: + break; + } + } + } + + *opt_count = count +1; + *optstring_len = length +1; +} + +static void optarg_copy(struct option *opts, char *optstring) +{ + struct optarg_entry *entry; + + list_for_each_entry(entry, &option_list, list) { + memcpy(opts, entry->opts, sizeof(struct option) * entry->count); + opts += entry->count; + + int i; + for (i = 0; i < entry->count; i++) { + switch (entry->opts[i].has_arg) { + case 0: /* no arguments */ + *optstring++ = (char)entry->opts[i].val; + break; + + case 1: /* has argument */ + *optstring++ = (char)entry->opts[i].val; + *optstring++ = ':'; + break; + + case 2: /* optional argument */ + *optstring++ = (char)entry->opts[i].val; + *optstring++ = ':'; + *optstring++ = ':'; + break; + + default: + break; + } + } + } + + memset(opts++, 0x00, sizeof(struct option)); + *optstring++ = '\0'; +} + +int optarg_parse(int argc, char * const argv[]) +{ + struct option *longopts; + char *optstring; + + int opt_count; + int optstring_len; + optarg_getsize(&opt_count, &optstring_len); + + longopts = malloc(sizeof(struct option) * opt_count); + if (longopts == NULL) + return -1; + + optstring = malloc(optstring_len); + if (optstring == NULL) { + free(longopts); + return -1; + } + + optarg_copy(longopts, optstring); + + int retval = 0; + int val = 0; + while (val != -1 && retval == 0) { + opterr = 1; /* print error message to stderr */ + val = getopt_long(argc, argv, optstring, longopts, NULL); + + if (val == 0x00) /* variable assigned (not supported) */ + continue; + + struct optarg_entry *entry; + list_for_each_entry(entry, &option_list, list) { + int ret = entry->parser_cb(val, optarg, entry->privdata); + + /* option recognized, with error */ + if (ret < 0) { + retval = ret; + break; + + /* option recognized, no error */ + } else if (ret == 0) { + break; + } + } + + if (val == -1) /* parsing completed */ + break; + + if (val == '?') { /* parsing error */ + retval = 1; + break; + } + } + + free(optstring); + free(longopts); + + return retval; +} diff --git a/linux/optarg.h b/linux/optarg.h new file mode 100644 index 0000000..4249d81 --- /dev/null +++ b/linux/optarg.h @@ -0,0 +1,15 @@ +#ifndef _OPTARG_H_ +#define _OPTARG_H_ + +#include + +int optarg_register(const struct option *opts, int count, + int (* parser_cb)(int val, const char *arg, void *privdata), + void *privdata); + + +void optarg_free(void); + +int optarg_parse(int argc, char * const argv[]); + +#endif /* _OPTARG_H_ */ diff --git a/linux/rules/99-i2c-tiny-usb.rules b/linux/rules/99-i2c-tiny-usb.rules new file mode 100644 index 0000000..82b5502 --- /dev/null +++ b/linux/rules/99-i2c-tiny-usb.rules @@ -0,0 +1,2 @@ +SUBSYSTEM=="usb", ATTR{product}=="i2c-tiny-usb", RUN+="/sbin/modprobe -b i2c-dev" +SUBSYSTEM=="i2c-dev", DRIVERS=="i2c-tiny-usb", MODE="0660", GROUP="dialout" diff --git a/linux/twb.c b/linux/twb.c deleted file mode 100644 index e5f7a4c..0000000 --- a/linux/twb.c +++ /dev/null @@ -1,329 +0,0 @@ -/*************************************************************************** - * Copyright (C) 10/2010 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 - -#include "filedata.h" -#include "list.h" -#include "twb.h" - -#define MIN(a, b) ((a) < (b) ? (a) : (b)) -#define ARRAY_SIZE(x) (sizeof(x) / sizeof(*x)) - -#define READ_BLOCK_SIZE 128 /* bytes in one flash/eeprom read request */ -#define WRITE_BLOCK_SIZE 16 /* bytes in one eeprom write request */ - -/* SLA+R */ -#define CMD_WAIT 0x00 -#define CMD_READ_VERSION 0x01 -#define CMD_READ_MEMORY 0x02 - -/* SLA+W */ -#define CMD_SWITCH_APPLICATION CMD_READ_VERSION -#define CMD_WRITE_MEMORY CMD_READ_MEMORY - -/* CMD_SWITCH_APPLICATION parameter */ -#define BOOTTYPE_BOOTLOADER 0x00 /* only in APP */ -#define BOOTTYPE_APPLICATION 0x80 - -/* CMD_{READ|WRITE}_* parameter */ -#define MEMTYPE_CHIPINFO 0x00 -#define MEMTYPE_FLASH 0x01 -#define MEMTYPE_EEPROM 0x02 -#define MEMTYPE_PARAMETERS 0x03 /* only in APP */ - -struct chipinfo { - uint8_t sig[3]; - const char name[16]; -}; - -static struct chipinfo chips[] = { - { { 0x1E, 0x93, 0x07 }, "AVR Mega 8" }, - { { 0x1E, 0x93, 0x0A }, "AVR Mega 88" }, - { { 0x1E, 0x94, 0x06 }, "AVR Mega 168" }, -}; - -static const char * twb_get_chipname(uint8_t *sig) -{ - int i; - 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; - } - - return "unknown"; -} - -static int twb_switch_application(struct twiboot *twb, uint8_t application) -{ - uint8_t cmd[] = { CMD_SWITCH_APPLICATION, application }; - - return (write(twb->fd, cmd, sizeof(cmd)) != sizeof(cmd)); -} - -static int twb_read_version(struct twiboot *twb) -{ - uint8_t cmd[] = { CMD_READ_VERSION }; - - if (write(twb->fd, cmd, sizeof(cmd)) != sizeof(cmd)) - return -1; - - memset(twb->version, 0, sizeof(twb->version)); - if (read(twb->fd, twb->version, sizeof(twb->version)) != sizeof(twb->version)) - return -1; - - int i; - for (i = 0; i < sizeof(twb->version); i++) - twb->version[i] &= ~0x80; - - return 0; -} - -static int twb_read_memory(struct twiboot *twb, uint8_t *buffer, uint8_t size, uint8_t memtype, uint16_t address) -{ - uint8_t cmd[] = { CMD_READ_MEMORY, memtype, (address >> 8) & 0xFF, (address & 0xFF) }; - if (write(twb->fd, cmd, sizeof(cmd)) != sizeof(cmd)) - return -1; - - return (read(twb->fd, buffer, size) != size); -} - -static int twb_write_memory(struct twiboot *twb, uint8_t *buffer, uint8_t size, uint8_t memtype, uint16_t address) -{ - int bufsize; - if (memtype == MEMTYPE_FLASH) { - if ((address & (twb->pagesize -1)) != 0x00) { - fprintf(stderr, "twb_write_memory(): address 0x%04x not aligned to pagesize 0x%02x\n", address, twb->pagesize); - return -1; - } - bufsize = 4 + twb->pagesize; - - } else { - bufsize = 4 + size; - } - - uint8_t *cmd = malloc(bufsize); - if (cmd == NULL) - return -1; - - cmd[0] = CMD_WRITE_MEMORY; - cmd[1] = memtype; - cmd[2] = (address >> 8) & 0xFF; - cmd[3] = (address & 0xFF); - memcpy(cmd +4, buffer, size); - - if (memtype == MEMTYPE_FLASH) { - memset(cmd +4 +size, 0xFF, twb->pagesize - size); - } - - int result = write(twb->fd, cmd, bufsize); - free(cmd); - - return (result != bufsize); -} - -static void twb_close_device(struct twiboot *twb) -{ - if (twb->connected) - close(twb->fd); - - if (twb->device != NULL) - free(twb->device); - - twb->device = NULL; -} - -static int twb_open_device(struct twiboot *twb) -{ - twb->fd = open(twb->device, O_RDWR); - if (twb->fd < 0) { - fprintf(stderr, "failed to open '%s': %s\n", twb->device, strerror(errno)); - return -1; - } - - unsigned long funcs; - if (ioctl(twb->fd, I2C_FUNCS, &funcs)) { - perror("ioctl(I2C_FUNCS)"); - close(twb->fd); - return -1; - } - - if (!(funcs & I2C_FUNC_I2C)) { - fprintf(stderr, "I2C_FUNC_I2C not supported on '%s'!\n", twb->device); - close(twb->fd); - return -1; - } - - if (ioctl(twb->fd, I2C_SLAVE, twb->address) < 0) { - fprintf(stderr, "failed to select slave address '%d': %s\n", twb->address, strerror(errno)); - close(twb->fd); - return -1; - } - - twb->connected = 1; - return 0; -} - -int twb_close(struct twiboot *twb) -{ - if (twb->connected) - twb_switch_application(twb, BOOTTYPE_APPLICATION); - - twb_close_device(twb); - return 0; -} - -int twb_open(struct twiboot *twb) -{ - if (twb_open_device(twb) != 0) - return -1; - - if (twb_switch_application(twb, BOOTTYPE_BOOTLOADER)) { - fprintf(stderr, "failed to switch to bootloader (invalid address?): %s\n", strerror(errno)); - twb_close(twb); - return -1; - } - - /* wait for watchdog and startup time */ - usleep(100000); - - if (twb_read_version(twb)) { - fprintf(stderr, "failed to get bootloader version: %s\n", strerror(errno)); - twb_close(twb); - return -1; - } - - uint8_t chipinfo[8]; - if (twb_read_memory(twb, chipinfo, sizeof(chipinfo), MEMTYPE_CHIPINFO, 0x0000)) { - fprintf(stderr, "failed to get chipinfo: %s\n", strerror(errno)); - twb_close(twb); - return -1; - } - - memcpy(twb->signature, chipinfo, sizeof(twb->signature)); - twb->chipname = twb_get_chipname(twb->signature); - - twb->pagesize = chipinfo[3]; - twb->flashsize = (chipinfo[4] << 8) + chipinfo[5]; - twb->eepromsize = (chipinfo[6] << 8) + chipinfo[7]; - - return 0; -} - -int twb_read(struct twiboot *twb, struct databuf *dbuf, int memtype) -{ - int pos = 0; - int size = (memtype == MEMTYPE_FLASH) ? twb->flashsize : twb->eepromsize; - - while (pos < size) { - if (twb->progress_cb) - twb->progress_cb(twb->progress_msg, pos, size); - - int len = MIN(READ_BLOCK_SIZE, size - pos); - if (twb_read_memory(twb, dbuf->data + pos, len, memtype, pos)) { - if (twb->progress_cb) - twb->progress_cb(twb->progress_msg, -1, -1); - - return -1; - } - - pos += len; - } - - if (twb->progress_cb) - twb->progress_cb(twb->progress_msg, pos, size); - - dbuf->length = pos; - return 0; -} - -int twb_write(struct twiboot *twb, struct databuf *dbuf, int memtype) -{ - int pos = 0; - - while (pos < dbuf->length) { - if (twb->progress_cb) - twb->progress_cb(twb->progress_msg, pos, dbuf->length); - - int len = (memtype == MEMTYPE_FLASH) ? twb->pagesize : WRITE_BLOCK_SIZE; - - len = MIN(len, dbuf->length - pos); - if (twb_write_memory(twb, dbuf->data + pos, len, memtype, pos)) { - if (twb->progress_cb) - twb->progress_cb(twb->progress_msg, -1, -1); - - return -1; - } - - pos += len; - } - - if (twb->progress_cb) - twb->progress_cb(twb->progress_msg, pos, dbuf->length); - - return 0; -} - -int twb_verify(struct twiboot *twb, struct databuf *dbuf, int memtype) -{ - int pos = 0; - uint8_t comp[READ_BLOCK_SIZE]; - - while (pos < dbuf->length) { - if (twb->progress_cb) - twb->progress_cb(twb->progress_msg, pos, dbuf->length); - - int len = MIN(READ_BLOCK_SIZE, dbuf->length - pos); - if (twb_read_memory(twb, comp, len, memtype, pos)) { - if (twb->progress_cb) - twb->progress_cb(twb->progress_msg, -1, -1); - - return -1; - } - - if (memcmp(comp, dbuf->data + pos, len) != 0x00) { - if (twb->progress_cb) - twb->progress_cb(twb->progress_msg, -1, -1); - - fprintf(stderr, "verify failed at page 0x%04x!!\n", pos); - return -1; - } - - pos += len; - } - - if (twb->progress_cb) - twb->progress_cb(twb->progress_msg, pos, dbuf->length); - - dbuf->length = pos; - return 0; -} diff --git a/linux/twb.h b/linux/twb.h deleted file mode 100644 index c26c48f..0000000 --- a/linux/twb.h +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef _TWB_H_ -#define _TWB_H_ - -#include - -struct twiboot { - char *device; - uint8_t address; - int fd; - int connected; - - char version[16]; - uint8_t signature[3]; - const char *chipname; - - uint8_t pagesize; - uint16_t flashsize; - uint16_t eepromsize; - - void (* progress_cb)(const char *msg, int pos, int max); - char *progress_msg; -}; - -int twb_open(struct twiboot *twb); -int twb_close(struct twiboot *twb); - -int twb_read(struct twiboot *twb, struct databuf *dbuf, int memtype); -int twb_verify(struct twiboot *twb, struct databuf *dbuf, int memtype); -int twb_write(struct twiboot *twb, struct databuf *dbuf, int memtype); - -#endif /* _TWIBOOT_H_ */ diff --git a/linux/twi.c b/linux/twi.c new file mode 100644 index 0000000..a0589b0 --- /dev/null +++ b/linux/twi.c @@ -0,0 +1,469 @@ +/*************************************************************************** +* Copyright (C) 10/2010 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 + +#include "chipinfo_avr.h" +#include "filedata.h" +#include "list.h" +#include "multiboot.h" +#include "optarg.h" + +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#define ARRAY_SIZE(x) (sizeof(x) / sizeof(*x)) + +#define TWI_DEFAULT_DEVICE "/dev/i2c-0" + +#define READ_BLOCK_SIZE 128 /* bytes in one flash/eeprom read request */ +#define WRITE_BLOCK_SIZE 16 /* bytes in one eeprom write request */ + +/* SLA+R */ +#define CMD_WAIT 0x00 +#define CMD_READ_VERSION 0x01 +#define CMD_READ_MEMORY 0x02 + +/* SLA+W */ +#define CMD_SWITCH_APPLICATION CMD_READ_VERSION +#define CMD_WRITE_MEMORY CMD_READ_MEMORY + +/* CMD_SWITCH_APPLICATION parameter */ +#define BOOTTYPE_BOOTLOADER 0x00 /* only in APP */ +#define BOOTTYPE_APPLICATION 0x80 + +/* CMD_{READ|WRITE}_* parameter */ +#define MEMTYPE_CHIPINFO 0x00 +#define MEMTYPE_FLASH 0x01 +#define MEMTYPE_EEPROM 0x02 +#define MEMTYPE_PARAMETERS 0x03 /* only in APP */ + +struct multiboot_ops twi_ops; + +struct twi_privdata { + char *device; + uint8_t address; + int fd; + int connected; + + uint8_t pagesize; + uint16_t flashsize; + uint16_t eepromsize; +}; + +static struct option twi_optargs[] = { + {"address", 1, 0, 'a'}, /* -a */ + {"device", 1, 0, 'd'}, /* [ -d ] */ +}; + +static int twi_switch_application(struct twi_privdata *twi, uint8_t application) +{ + uint8_t cmd[] = { CMD_SWITCH_APPLICATION, application }; + + return (write(twi->fd, cmd, sizeof(cmd)) != sizeof(cmd)); +} + +static int twi_read_version(struct twi_privdata *twi, char *version, int length) +{ + uint8_t cmd[] = { CMD_READ_VERSION }; + + if (write(twi->fd, cmd, sizeof(cmd)) != sizeof(cmd)) + return -1; + + memset(version, 0, length); + if (read(twi->fd, version, length) != length) + return -1; + + int i; + for (i = 0; i < length; i++) + version[i] &= ~0x80; + + return 0; +} + +static int twi_read_memory(struct twi_privdata *twi, uint8_t *buffer, uint8_t size, uint8_t memtype, uint16_t address) +{ + uint8_t cmd[] = { CMD_READ_MEMORY, memtype, (address >> 8) & 0xFF, (address & 0xFF) }; + if (write(twi->fd, cmd, sizeof(cmd)) != sizeof(cmd)) + return -1; + + return (read(twi->fd, buffer, size) != size); +} + +static int twi_write_memory(struct twi_privdata *twi, uint8_t *buffer, uint8_t size, uint8_t memtype, uint16_t address) +{ + int bufsize; + if (memtype == MEMTYPE_FLASH) { + if ((address & (twi->pagesize -1)) != 0x00) { + fprintf(stderr, "twi_write_memory(): address 0x%04x not aligned to pagesize 0x%02x\n", address, twi->pagesize); + return -1; + } + bufsize = 4 + twi->pagesize; + + } else { + bufsize = 4 + size; + } + + uint8_t *cmd = malloc(bufsize); + if (cmd == NULL) + return -1; + + cmd[0] = CMD_WRITE_MEMORY; + cmd[1] = memtype; + cmd[2] = (address >> 8) & 0xFF; + cmd[3] = (address & 0xFF); + memcpy(cmd +4, buffer, size); + + if (memtype == MEMTYPE_FLASH) { + memset(cmd +4 +size, 0xFF, twi->pagesize - size); + } + + int result = write(twi->fd, cmd, bufsize); + free(cmd); + + return (result != bufsize); +} + +static void twi_close_device(struct twi_privdata *twi) +{ + if (twi->connected) + close(twi->fd); + + twi->connected = 0; +} + +static int twi_open_device(struct twi_privdata *twi) +{ + twi->fd = open(twi->device, O_RDWR); + if (twi->fd < 0) { + fprintf(stderr, "failed to open '%s': %s\n", twi->device, strerror(errno)); + return -1; + } + + unsigned long funcs; + if (ioctl(twi->fd, I2C_FUNCS, &funcs)) { + perror("ioctl(I2C_FUNCS)"); + close(twi->fd); + return -1; + } + + if (!(funcs & I2C_FUNC_I2C)) { + fprintf(stderr, "I2C_FUNC_I2C not supported on '%s'!\n", twi->device); + close(twi->fd); + return -1; + } + + if (ioctl(twi->fd, I2C_SLAVE, twi->address) < 0) { + fprintf(stderr, "failed to select slave address '%d': %s\n", twi->address, strerror(errno)); + close(twi->fd); + return -1; + } + + twi->connected = 1; + return 0; +} + +static int twi_close(struct multiboot *mboot) +{ + struct twi_privdata *twi = (struct twi_privdata *)mboot->privdata; + + if (twi->connected) + twi_switch_application(twi, BOOTTYPE_APPLICATION); + + twi_close_device(twi); + return 0; +} + +static int twi_open(struct multiboot *mboot) +{ + struct twi_privdata *twi = (struct twi_privdata *)mboot->privdata; + + if (twi->address == 0) { + fprintf(stderr, "abort: no address given\n"); + return -1; + } + + if (twi->device == NULL) { + twi->device = strdup(TWI_DEFAULT_DEVICE); + if (twi->device == NULL) { + perror("strdup()"); + return -1; + } + } + + if (twi_open_device(twi) != 0) + return -1; + + if (twi_switch_application(twi, BOOTTYPE_BOOTLOADER)) { + fprintf(stderr, "failed to switch to bootloader (invalid address?): %s\n", strerror(errno)); + twi_close(mboot); + return -1; + } + + /* wait for watchdog and startup time */ + usleep(100000); + + char version[16]; + if (twi_read_version(twi, version, sizeof(version))) { + fprintf(stderr, "failed to get bootloader version: %s\n", strerror(errno)); + twi_close(mboot); + return -1; + } + + uint8_t chipinfo[8]; + if (twi_read_memory(twi, chipinfo, sizeof(chipinfo), MEMTYPE_CHIPINFO, 0x0000)) { + fprintf(stderr, "failed to get chipinfo: %s\n", strerror(errno)); + twi_close(mboot); + return -1; + } + + const char *chipname = chipinfo_get_avr_name(chipinfo); + + twi->pagesize = chipinfo[3]; + twi->flashsize = (chipinfo[4] << 8) + chipinfo[5]; + twi->eepromsize = (chipinfo[6] << 8) + chipinfo[7]; + + printf("device : %-16s (address: 0x%02X)\n", twi->device, twi->address); + printf("version : %-16s (sig: 0x%02x 0x%02x 0x%02x => %s)\n", version, chipinfo[0], chipinfo[1], chipinfo[2], chipname); + printf("flash size : 0x%04x / %5d (0x%02x bytes/page)\n", twi->flashsize, twi->flashsize, twi->pagesize); + printf("eeprom size : 0x%04x / %5d\n", twi->eepromsize, twi->eepromsize); + + return 0; +} + +static int twi_read(struct multiboot *mboot, struct databuf *dbuf, int memtype) +{ + struct twi_privdata *twi = (struct twi_privdata *)mboot->privdata; + char *progress_msg = (memtype == MEMTYPE_FLASH) ? "reading flash" : "reading eeprom"; + + int pos = 0; + int size = (memtype == MEMTYPE_FLASH) ? twi->flashsize : twi->eepromsize; + while (pos < size) { + mboot->progress_cb(progress_msg, pos, size); + + int len = MIN(READ_BLOCK_SIZE, size - pos); + if (twi_read_memory(twi, dbuf->data + pos, len, memtype, pos)) { + mboot->progress_cb(progress_msg, -1, -1); + return -1; + } + + pos += len; + } + + dbuf->length = pos; + + mboot->progress_cb(progress_msg, pos, size); + return 0; +} + +static int twi_write(struct multiboot *mboot, struct databuf *dbuf, int memtype) +{ + struct twi_privdata *twi = (struct twi_privdata *)mboot->privdata; + char *progress_msg = (memtype == MEMTYPE_FLASH) ? "writing flash" : "writing eeprom"; + + int pos = 0; + while (pos < dbuf->length) { + mboot->progress_cb(progress_msg, pos, dbuf->length); + + int len = (memtype == MEMTYPE_FLASH) ? twi->pagesize : WRITE_BLOCK_SIZE; + + len = MIN(len, dbuf->length - pos); + if (twi_write_memory(twi, dbuf->data + pos, len, memtype, pos)) { + mboot->progress_cb(progress_msg, -1, -1); + return -1; + } + + pos += len; + } + + mboot->progress_cb(progress_msg, pos, dbuf->length); + return 0; +} + +static int twi_verify(struct multiboot *mboot, struct databuf *dbuf, int memtype) +{ + struct twi_privdata *twi = (struct twi_privdata *)mboot->privdata; + char *progress_msg = (memtype == MEMTYPE_FLASH) ? "verifing flash" : "verifing eeprom"; + + int pos = 0; + uint8_t comp[READ_BLOCK_SIZE]; + while (pos < dbuf->length) { + mboot->progress_cb(progress_msg, pos, dbuf->length); + + int len = MIN(READ_BLOCK_SIZE, dbuf->length - pos); + if (twi_read_memory(twi, comp, len, memtype, pos)) { + mboot->progress_cb(progress_msg, -1, -1); + return -1; + } + + if (memcmp(comp, dbuf->data + pos, len) != 0x00) { + mboot->progress_cb(progress_msg, -1, -1); + fprintf(stderr, "verify failed at page 0x%04x!!\n", pos); + return -1; + } + + pos += len; + } + + dbuf->length = pos; + + mboot->progress_cb(progress_msg, pos, dbuf->length); + return 0; +} + +static int twi_optarg_cb(int val, const char *arg, void *privdata) +{ + struct twi_privdata *twi = (struct twi_privdata *)privdata; + + switch (val) { + case 'a': /* address */ + { + char *endptr; + twi->address = strtol(arg, &endptr, 16); + if (*endptr != '\0' || twi->address < 0x01 || twi->address > 0x7F) { + fprintf(stderr, "invalid address: '%s'\n", arg); + return -1; + } + } + break; + + case 'd': /* device */ + { + if (twi->device != NULL) { + fprintf(stderr, "invalid device: '%s'\n", optarg); + return -1; + } + + twi->device = strdup(optarg); + if (twi->device == NULL) { + perror("strdup()"); + return -1; + } + } + break; + + case 'h': + case '?': /* error */ + fprintf(stderr, "Usage: twiboot [options]\n" + " -a
- selects i2c address (0x01 - 0x7F)\n" + " -d - selects i2c device (default: /dev/i2c-0)\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: twiboot -a 0x22 -w flash:blmc.hex -w flash:blmc_eeprom.hex\n" + "\n"); + return -1; + + default: + return 1; + } + + return 0; +} + +static struct multiboot * twi_alloc(void) +{ + struct multiboot * mboot = malloc(sizeof(struct multiboot)); + if (mboot == NULL) + return NULL; + + memset(mboot, 0x00, sizeof(struct multiboot)); + mboot->ops = &twi_ops; + + struct twi_privdata *twi = malloc(sizeof(struct twi_privdata)); + if (twi == NULL) { + free(mboot); + return NULL; + } + + memset(twi, 0x00, sizeof(struct twi_privdata)); + twi->device = NULL; + twi->address = 0; + + optarg_register(twi_optargs, ARRAY_SIZE(twi_optargs), twi_optarg_cb, (void *)twi); + + mboot->privdata = twi; + return mboot; +} + +static void twi_free(struct multiboot *mboot) +{ + struct twi_privdata *twi = (struct twi_privdata *)mboot->privdata; + + if (twi->device != NULL) + free(twi->device); + + free(twi); + free(mboot); +} + +static int twi_get_memtype(struct multiboot *mboot, const char *memname) +{ + if (strcmp(memname, "flash") == 0) + return MEMTYPE_FLASH; + + else if (strcmp(memname, "eeprom") == 0) + return MEMTYPE_EEPROM; + + return -1; +} + +static int twi_get_memsize(struct multiboot *mboot, int memtype) +{ + struct twi_privdata *twi = (struct twi_privdata *)mboot->privdata; + + if (!twi->connected) + return 0; + + switch (memtype) { + case MEMTYPE_FLASH: + return twi->flashsize; + + case MEMTYPE_EEPROM: + return twi->eepromsize; + + default: + return 0; + } +} + +struct multiboot_ops twi_ops = { + .alloc = twi_alloc, + .free = twi_free, + .get_memtype = twi_get_memtype, + .get_memsize = twi_get_memsize, + + .open = twi_open, + .close = twi_close, + .read = twi_read, + .write = twi_write, + .verify = twi_verify, +}; diff --git a/linux/twiboot.c b/linux/twiboot.c deleted file mode 100644 index 4d46834..0000000 --- a/linux/twiboot.c +++ /dev/null @@ -1,365 +0,0 @@ -/*************************************************************************** - * Copyright (C) 10/2010 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 "filedata.h" -#include "list.h" -#include "twb.h" - -#define OP_MODE_READ 0x01 -#define OP_MODE_WRITE 0x02 -#define OP_TYPE_FLASH 0x01 -#define OP_TYPE_EEPROM 0x02 - -struct operation { - struct list_head list; - - char *filename; - int flags; - - int mode; - int memtype; -}; - -static LIST_HEAD(operation_list); - -static struct option opts[] = { - {"address", 1, 0, 'a'}, // -a - {"device", 1, 0, 'd'}, // [ -d ] - {"help", 0, 0, 'h'}, // [ -h ] - {"progress", 1, 0, 'p'}, // [ -p <0|1|2> ] - {"read", 1, 0, 'r'}, // [ -r : ] - {"write", 1, 0, 'w'}, // [ -w : ] - {"no-verify", 0, 0, 'n'}, // [ -n ] - {0, 0, 0, 0} -}; - -static struct operation * alloc_operation(const char *arg) -{ - struct operation *op = malloc(sizeof(struct operation)); - if (op == NULL) { - perror("malloc()"); - return NULL; - } - - if (strncmp(arg, "flash:", 6) == 0) { - op->memtype = OP_TYPE_FLASH; - op->filename = strdup(arg + 6); - - } else if (strncmp(arg, "eeprom:", 7) == 0) { - op->memtype = OP_TYPE_EEPROM; - op->filename = strdup(arg + 7); - - } else { - fprintf(stderr, "invalid memtype: '%s'\n", arg); - return NULL; - } - - return op; -} - -static void progress_mode1_cb(const char *msg, int pos, int size) -{ - if (pos != -1 && size != -1) { - char stars[50]; - - int i; - int count = (pos * sizeof(stars) / size); - for (i = 0; i < sizeof(stars); i++) - stars[i] = (i < count) ? '*' : ' '; - - printf("%-15s: [%s] (%d)\r", msg, stars, pos); - } - - if (pos == size) - printf("\n"); -} - -static void progress_mode2_cb(const char *msg, int pos, int size) -{ - static int old_count; - - if (pos != -1 && size != -1) { - if (pos == 0) { - old_count = 0; - printf("%-15s: [", msg); - - } else if (pos <=size) { - int i; - int count = (pos * 50 / size); - for (i = old_count; i < count; i++) - printf("*"); - - old_count = count; - - if (pos == size) { - printf("] (%d)\n", pos); - } - } - } -} - -int main(int argc, char *argv[]) -{ - struct twiboot twb; - int verify = 1, progress = 1; - - memset(&twb, 0, sizeof(struct twiboot)); - - int arg = 0, code = 0, abort = 0; - while (code != -1) { - code = getopt_long(argc, argv, "a:d:hnp:r:w:", opts, &arg); - - switch (code) { - case 'a': /* address */ - { - char *endptr; - twb.address = strtol(optarg, &endptr, 16); - if (*endptr != '\0' || twb.address < 0x01 || twb.address > 0x7F) { - fprintf(stderr, "invalid address: '%s'\n", optarg); - abort = 1; - break; - } - break; - } - - case 'd': /* device */ - if (twb.device != NULL) { - fprintf(stderr, "invalid device: '%s'\n", optarg); - abort = 1; - break; - } - - twb.device = strdup(optarg); - if (twb.device == NULL) { - perror("strdup()"); - abort = 1; - break; - } - break; - - case 'r': /* read */ - { - struct operation *op = alloc_operation(optarg); - if (op != NULL) { - op->mode = OP_MODE_READ; - list_add_tail(&op->list, &operation_list); - - } else { - abort = 1; - } - break; - } - - case 'w': /* write */ - { - struct operation *op = alloc_operation(optarg); - if (op != NULL) { - op->mode = OP_MODE_WRITE; - list_add_tail(&op->list, &operation_list); - - } else { - abort = 1; - } - break; - } - - case 'n': /* no verify */ - verify = 0; - break; - - case 'p': /* progress bar mode */ - { - if (*optarg >= '0' && *optarg <= '2') { - progress = *optarg - '0'; - - } else { - fprintf(stderr, "invalid progress bar mode: '%s'\n", optarg); - abort = 1; - } - break; - } - progress = 0; - break; - - case 'h': - case '?': /* error */ - fprintf(stderr, "Usage: twiboot [options]\n" - " -a
- selects i2c address (0x01 - 0x7F)\n" - " -d - selects i2c device (default: /dev/i2c-0)\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: twiboot -a 0x22 -w flash:blmc.hex -w flash:blmc_eeprom.hex\n" - "\n"); - abort = 1; - break; - - default: /* unknown / all options parsed */ - break; - } - } - - if (twb.address == 0) { - fprintf(stderr, "abort: no address given\n"); - abort = 1; - } - - if (twb.device == NULL) { - twb.device = strdup("/dev/i2c-0"); - if (twb.device == NULL) { - perror("strdup()"); - abort = 1; - } - } - - if (!abort) { - if (twb_open(&twb) != 0x00) - abort = 1; - } - - if (!abort) { - printf("device : %-16s (address: 0x%02X)\n", twb.device, twb.address); - printf("version : %-16s (sig: 0x%02x 0x%02x 0x%02x => %s)\n", twb.version, twb.signature[0], twb.signature[1], twb.signature[2], twb.chipname); - printf("flash size : 0x%04x / %5d (0x%02x bytes/page)\n", twb.flashsize, twb.flashsize, twb.pagesize); - printf("eeprom size : 0x%04x / %5d\n", twb.eepromsize, twb.eepromsize); - - if (progress) { - setbuf(stdout, NULL); - twb.progress_cb = (progress == 1) ? progress_mode1_cb : progress_mode2_cb; - } - - struct operation *op; - list_for_each_entry(op, &operation_list, list) { - abort = 1; - if (op->mode == OP_MODE_READ) { - struct databuf *dbuf; - int result; - - if (op->memtype == OP_TYPE_FLASH) { - twb.progress_msg = "reading flash"; - result = dbuf_alloc(&dbuf, twb.flashsize); - } else if (op->memtype == OP_TYPE_EEPROM) { - twb.progress_msg = "reading eeprom"; - result = dbuf_alloc(&dbuf, twb.eepromsize); - } - - if (result != 0x00) - break; - - result = twb_read(&twb, dbuf, op->memtype); - if (result != 0x00) { - fprintf(stderr, "failed to read from device\n"); - dbuf_free(dbuf); - break; - } - - result = file_write(op->filename, dbuf); - if (result != 0x00) { - fprintf(stderr, "failed to write file '%s'\n", op->filename); - dbuf_free(dbuf); - break; - } - - dbuf_free(dbuf); - - } else if (op->mode == OP_MODE_WRITE) { - struct databuf *dbuf; - unsigned int size; - int result; - - result = file_getsize(op->filename, &size); - if (result != 0x00) - break; - - result = dbuf_alloc(&dbuf, size); - if (result != 0x00) - break; - - result = file_read(op->filename, dbuf); - if (result != 0x00) { - fprintf(stderr, "failed to read file '%s'\n", op->filename); - dbuf_free(dbuf); - break; - } - - if (op->memtype == OP_TYPE_FLASH) { - twb.progress_msg = "writing flash"; - - if (dbuf->length > twb.flashsize) { - fprintf(stderr, "invalid flash size: 0x%04x > 0x%04x\n", dbuf->length, twb.flashsize); - dbuf_free(dbuf); - break; - } - - } else if (op->memtype == OP_TYPE_EEPROM) { - twb.progress_msg = "writing eeprom"; - - if (dbuf->length > twb.eepromsize) { - fprintf(stderr, "invalid eeprom size: 0x%04x > 0x%04x\n", dbuf->length, twb.eepromsize); - dbuf_free(dbuf); - break; - } - } - - result = twb_write(&twb, dbuf, op->memtype); - if (result != 0x00) { - fprintf(stderr, "failed to write to device\n"); - dbuf_free(dbuf); - break; - } - - if (verify) { - if (op->memtype == OP_TYPE_FLASH) { - twb.progress_msg = "verifing flash"; - } else if (op->memtype == OP_TYPE_EEPROM) { - twb.progress_msg = "verifing eeprom"; - } - - result = twb_verify(&twb, dbuf, op->memtype); - if (result != 0) { - fprintf(stderr, "failed to verify\n"); - dbuf_free(dbuf); - break; - } - } - - dbuf_free(dbuf); - } - abort = 0; - } - } - - struct operation *op, *tmp; - list_for_each_entry_safe(op, tmp, &operation_list, list) { - free(op->filename); - free(op); - } - - twb_close(&twb); - - return abort; -}