replace linux application
This commit is contained in:
parent
9424e20ea7
commit
ca2a0a99ae
@ -1,4 +1,5 @@
|
|||||||
TARGET = twiboot
|
TARGET = twiboot
|
||||||
|
TARGET2 = mpmboot
|
||||||
|
|
||||||
CFLAGS = -Wall -Wno-unused-result -O2 -MMD -MP -MF $(*F).d
|
CFLAGS = -Wall -Wno-unused-result -O2 -MMD -MP -MF $(*F).d
|
||||||
|
|
||||||
@ -11,12 +12,13 @@ all: $(TARGET)
|
|||||||
$(TARGET): $(SRC:.c=.o)
|
$(TARGET): $(SRC:.c=.o)
|
||||||
@echo " Linking file: $@"
|
@echo " Linking file: $@"
|
||||||
@$(CC) $(CFLAGS) $^ -o $@ $(LDFLAGS) > /dev/null
|
@$(CC) $(CFLAGS) $^ -o $@ $(LDFLAGS) > /dev/null
|
||||||
|
@ln -sf $@ $(TARGET2)
|
||||||
|
|
||||||
%.o: %.c
|
%.o: %.c
|
||||||
@echo " Building file: $<"
|
@echo " Building file: $<"
|
||||||
@$(CC) -c $(CFLAGS) $< -o $@
|
@$(CC) -c $(CFLAGS) $< -o $@
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -rf $(TARGET) *.o *.d
|
rm -rf $(TARGET) $(TARGET2) *.o *.d
|
||||||
|
|
||||||
-include $(shell find . -name \*.d 2> /dev/null)
|
-include $(shell find . -name \*.d 2> /dev/null)
|
||||||
|
52
linux/chipinfo_avr.c
Normal file
52
linux/chipinfo_avr.c
Normal file
@ -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 <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#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";
|
||||||
|
}
|
8
linux/chipinfo_avr.h
Normal file
8
linux/chipinfo_avr.h
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
#ifndef _CHIPINFO_H_
|
||||||
|
#define _CHIPINFO_H_
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
const char * chipinfo_get_avr_name(const uint8_t *sig);
|
||||||
|
|
||||||
|
#endif /* _CHIPINFO_H_ */
|
@ -1,21 +1,21 @@
|
|||||||
/***************************************************************************
|
/***************************************************************************
|
||||||
* Copyright (C) 10/2010 by Olaf Rempel *
|
* Copyright (C) 10/2010 by Olaf Rempel *
|
||||||
* razzor@kopf-tisch.de *
|
* razzor@kopf-tisch.de *
|
||||||
* *
|
* *
|
||||||
* This program is free software; you can redistribute it and/or modify *
|
* 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 *
|
* it under the terms of the GNU General Public License as published by *
|
||||||
* the Free Software Foundation; version 2 of the License, *
|
* the Free Software Foundation; version 2 of the License, *
|
||||||
* *
|
* *
|
||||||
* This program is distributed in the hope that it will be useful, *
|
* This program is distributed in the hope that it will be useful, *
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||||
* GNU General Public License for more details. *
|
* GNU General Public License for more details. *
|
||||||
* *
|
* *
|
||||||
* You should have received a copy of the GNU General Public License *
|
* You should have received a copy of the GNU General Public License *
|
||||||
* along with this program; if not, write to the *
|
* along with this program; if not, write to the *
|
||||||
* Free Software Foundation, Inc., *
|
* Free Software Foundation, Inc., *
|
||||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
|
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
|
||||||
***************************************************************************/
|
***************************************************************************/
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
@ -31,18 +31,18 @@
|
|||||||
#define FILETYPE_BINARY 1
|
#define FILETYPE_BINARY 1
|
||||||
#define FILETYPE_INTELHEX 2
|
#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);
|
struct databuf *dbuf = malloc(sizeof(struct databuf) + size);
|
||||||
if (*dbuf == NULL) {
|
if (dbuf == NULL) {
|
||||||
perror("dbuf_alloc");
|
perror("dbuf_alloc");
|
||||||
return -1;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
memset((*dbuf)->data, 0xFF, size);
|
memset(dbuf->data, 0xFF, size);
|
||||||
(*dbuf)->size = size;
|
dbuf->size = size;
|
||||||
(*dbuf)->length = 0;
|
dbuf->length = 0;
|
||||||
return 0;
|
return dbuf;
|
||||||
}
|
}
|
||||||
|
|
||||||
void dbuf_free(struct databuf *dbuf)
|
void dbuf_free(struct databuf *dbuf)
|
||||||
|
@ -9,7 +9,7 @@ struct databuf {
|
|||||||
uint8_t data[0];
|
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);
|
void dbuf_free(struct databuf *dbuf);
|
||||||
|
|
||||||
int file_getsize(const char *filename, uint32_t *size);
|
int file_getsize(const char *filename, uint32_t *size);
|
||||||
|
635
linux/mpm.c
Normal file
635
linux/mpm.c
Normal file
@ -0,0 +1,635 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <termios.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
|
||||||
|
#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 <addr> */
|
||||||
|
{"device", 1, 0, 'd'}, /* [ -d <device> ] */
|
||||||
|
};
|
||||||
|
|
||||||
|
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 <address> - selects mpm address (0x01 - 0xFF)\n"
|
||||||
|
" -d <device> - selects mpm device\n"
|
||||||
|
" -r <flash|eeprom>:<file> - reads flash/eeprom to file (.bin | .hex | -)\n"
|
||||||
|
" -w <flash|eeprom>:<file> - 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,
|
||||||
|
};
|
320
linux/multiboot.c
Normal file
320
linux/multiboot.c
Normal file
@ -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 <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <getopt.h>
|
||||||
|
|
||||||
|
#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 <flash|eeprom>:<file.hex> ] */
|
||||||
|
{"write", 1, 0, 'w'}, /* [ -w <flash|eeprom>:<file.hex> ] */
|
||||||
|
{"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;
|
||||||
|
}
|
33
linux/multiboot.h
Normal file
33
linux/multiboot.h
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
#ifndef _MULTIBOOT_H_
|
||||||
|
#define _MULTIBOOT_H_
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#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_ */
|
193
linux/optarg.c
Normal file
193
linux/optarg.c
Normal file
@ -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 <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <getopt.h>
|
||||||
|
|
||||||
|
#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;
|
||||||
|
}
|
15
linux/optarg.h
Normal file
15
linux/optarg.h
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
#ifndef _OPTARG_H_
|
||||||
|
#define _OPTARG_H_
|
||||||
|
|
||||||
|
#include <getopt.h>
|
||||||
|
|
||||||
|
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_ */
|
2
linux/rules/99-i2c-tiny-usb.rules
Normal file
2
linux/rules/99-i2c-tiny-usb.rules
Normal file
@ -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"
|
329
linux/twb.c
329
linux/twb.c
@ -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 <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <dirent.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <errno.h>
|
|
||||||
#include <sys/ioctl.h>
|
|
||||||
|
|
||||||
#include <linux/i2c.h>
|
|
||||||
#include <linux/i2c-dev.h>
|
|
||||||
|
|
||||||
#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;
|
|
||||||
}
|
|
31
linux/twb.h
31
linux/twb.h
@ -1,31 +0,0 @@
|
|||||||
#ifndef _TWB_H_
|
|
||||||
#define _TWB_H_
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
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_ */
|
|
469
linux/twi.c
Normal file
469
linux/twi.c
Normal file
@ -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 <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
|
||||||
|
#include <linux/i2c.h>
|
||||||
|
#include <linux/i2c-dev.h>
|
||||||
|
|
||||||
|
#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 <addr> */
|
||||||
|
{"device", 1, 0, 'd'}, /* [ -d <device> ] */
|
||||||
|
};
|
||||||
|
|
||||||
|
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 <address> - selects i2c address (0x01 - 0x7F)\n"
|
||||||
|
" -d <device> - selects i2c device (default: /dev/i2c-0)\n"
|
||||||
|
" -r <flash|eeprom>:<file> - reads flash/eeprom to file (.bin | .hex | -)\n"
|
||||||
|
" -w <flash|eeprom>:<file> - 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,
|
||||||
|
};
|
365
linux/twiboot.c
365
linux/twiboot.c
@ -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 <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#include <getopt.h>
|
|
||||||
|
|
||||||
#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 <addr>
|
|
||||||
{"device", 1, 0, 'd'}, // [ -d <device> ]
|
|
||||||
{"help", 0, 0, 'h'}, // [ -h ]
|
|
||||||
{"progress", 1, 0, 'p'}, // [ -p <0|1|2> ]
|
|
||||||
{"read", 1, 0, 'r'}, // [ -r <flash|eeprom>:<file.hex> ]
|
|
||||||
{"write", 1, 0, 'w'}, // [ -w <flash|eeprom>:<file.hex> ]
|
|
||||||
{"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 <address> - selects i2c address (0x01 - 0x7F)\n"
|
|
||||||
" -d <device> - selects i2c device (default: /dev/i2c-0)\n"
|
|
||||||
" -r <flash|eeprom>:<file> - reads flash/eeprom to file (.bin | .hex | -)\n"
|
|
||||||
" -w <flash|eeprom>:<file> - 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;
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user