add linux tool
This commit is contained in:
parent
8a98e09c46
commit
6244c44e65
3
linux/.gitignore
vendored
Normal file
3
linux/.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
*.o
|
||||||
|
*.d
|
||||||
|
twiboot
|
22
linux/Makefile
Normal file
22
linux/Makefile
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
TARGET = twiboot
|
||||||
|
|
||||||
|
CFLAGS = -Wall -Wno-unused-result -O2 -MMD -MP -MF $(*F).d
|
||||||
|
|
||||||
|
# ------
|
||||||
|
|
||||||
|
SRC := $(wildcard *.c)
|
||||||
|
|
||||||
|
all: $(TARGET)
|
||||||
|
|
||||||
|
$(TARGET): $(SRC:.c=.o)
|
||||||
|
@echo " Linking file: $@"
|
||||||
|
@$(CC) $(CFLAGS) $^ -o $@ $(LDFLAGS) > /dev/null
|
||||||
|
|
||||||
|
%.o: %.c
|
||||||
|
@echo " Building file: $<"
|
||||||
|
@$(CC) -c $(CFLAGS) $< -o $@
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -rf $(TARGET) *.o *.d
|
||||||
|
|
||||||
|
-include $(shell find . -name \*.d 2> /dev/null)
|
423
linux/filedata.c
Normal file
423
linux/filedata.c
Normal file
@ -0,0 +1,423 @@
|
|||||||
|
/***************************************************************************
|
||||||
|
* 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/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
|
||||||
|
#include "twiboot.h"
|
||||||
|
|
||||||
|
#define FILETYPE_UNKNOWN 0
|
||||||
|
#define FILETYPE_BINARY 1
|
||||||
|
#define FILETYPE_INTELHEX 2
|
||||||
|
|
||||||
|
int dbuf_alloc(struct databuf **dbuf, uint32_t size)
|
||||||
|
{
|
||||||
|
*dbuf = malloc(sizeof(struct databuf) + size);
|
||||||
|
if (*dbuf == NULL) {
|
||||||
|
perror("dbuf_alloc");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset((*dbuf)->data, 0xFF, size);
|
||||||
|
(*dbuf)->size = size;
|
||||||
|
(*dbuf)->length = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void dbuf_free(struct databuf *dbuf)
|
||||||
|
{
|
||||||
|
free(dbuf);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dbuf_dump(struct databuf *dbuf)
|
||||||
|
{
|
||||||
|
int pos = 0, oldskip = 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, " ");
|
||||||
|
|
||||||
|
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++] = '.';
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
} else if (skip == 1 && oldskip == 0) {
|
||||||
|
printf("****\n");
|
||||||
|
oldskip = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct stat filestat;
|
||||||
|
if (fstat(fd, &filestat) < 0) {
|
||||||
|
perror("binfile_getsize(): fstat()");
|
||||||
|
close(fd);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
*size = filestat.st_size;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
ssize_t readsize = read(fd, dbuf->data, dbuf->size);
|
||||||
|
if (readsize <= 0) {
|
||||||
|
perror("binfile_read(): read()");
|
||||||
|
close(fd);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
dbuf->length = readsize;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ihex_record {
|
||||||
|
uint8_t byte_count;
|
||||||
|
uint16_t address;
|
||||||
|
uint8_t type;
|
||||||
|
|
||||||
|
uint8_t *data;
|
||||||
|
uint8_t chksum;
|
||||||
|
};
|
||||||
|
|
||||||
|
static uint8_t hex2byte(const char *ptr)
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int hexfile_getrecord(FILE *stream, struct ihex_record *record)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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++;
|
||||||
|
|
||||||
|
uint8_t chksum = 0x00;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
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];
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int hexfile_read(const char *filename, struct databuf *dbuf)
|
||||||
|
{
|
||||||
|
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));
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(&dbuf->data[record.address], record.data, record.byte_count);
|
||||||
|
dbuf->length = record.address + record.byte_count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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_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];
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int get_filetype(const char *filename)
|
||||||
|
{
|
||||||
|
const char *ext = filename + (strlen(filename) -4);
|
||||||
|
|
||||||
|
if (ext < filename)
|
||||||
|
return FILETYPE_UNKNOWN;
|
||||||
|
|
||||||
|
if (strncmp(ext, ".bin", 4) == 0)
|
||||||
|
return FILETYPE_BINARY;
|
||||||
|
|
||||||
|
if (strncmp(ext, ".hex", 4) == 0)
|
||||||
|
return FILETYPE_INTELHEX;
|
||||||
|
|
||||||
|
return FILETYPE_UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
int file_getsize(const char *filename, uint32_t *size)
|
||||||
|
{
|
||||||
|
switch (get_filetype(filename)) {
|
||||||
|
case FILETYPE_BINARY:
|
||||||
|
return binfile_getsize(filename, size);
|
||||||
|
|
||||||
|
case FILETYPE_INTELHEX:
|
||||||
|
return hexfile_getsize(filename, size);
|
||||||
|
|
||||||
|
default:
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int file_read(const char *filename, struct databuf *dbuf)
|
||||||
|
{
|
||||||
|
switch (get_filetype(filename)) {
|
||||||
|
case FILETYPE_BINARY:
|
||||||
|
return binfile_read(filename, dbuf);
|
||||||
|
|
||||||
|
case FILETYPE_INTELHEX:
|
||||||
|
return hexfile_read(filename, dbuf);
|
||||||
|
|
||||||
|
default:
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int file_write(const char *filename, struct databuf *dbuf)
|
||||||
|
{
|
||||||
|
if (strncmp(filename, "-", 1) == 0) {
|
||||||
|
dbuf_dump(dbuf);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (get_filetype(filename)) {
|
||||||
|
case FILETYPE_BINARY:
|
||||||
|
return binfile_write(filename, dbuf);
|
||||||
|
|
||||||
|
case FILETYPE_INTELHEX:
|
||||||
|
return hexfile_write(filename, dbuf);
|
||||||
|
|
||||||
|
default:
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
268
linux/list.h
Normal file
268
linux/list.h
Normal file
@ -0,0 +1,268 @@
|
|||||||
|
#ifndef _LIST_H_
|
||||||
|
#define _LIST_H_
|
||||||
|
|
||||||
|
/*
|
||||||
|
* stolen from linux kernel 2.6.11 (http://kernel.org/)
|
||||||
|
* linux/include/linux/stddef.h (offsetoff)
|
||||||
|
* linux/include/linux/kernel.h (container_of)
|
||||||
|
* linux/include/linux/list.h (*list*)
|
||||||
|
* linux/include/linux/netfilter_ipv4/listhelp.h (LIST_FIND)
|
||||||
|
*
|
||||||
|
* modified by Olaf Rempel <razzor@kopf-tisch.de>
|
||||||
|
*/
|
||||||
|
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
|
||||||
|
|
||||||
|
#define container_of(ptr, type, member) ({ \
|
||||||
|
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
|
||||||
|
(type *)( (char *)__mptr - offsetof(type,member) );})
|
||||||
|
|
||||||
|
struct list_head {
|
||||||
|
struct list_head *next, *prev;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define LIST_HEAD_INIT(name) { &(name), &(name) }
|
||||||
|
|
||||||
|
#define LIST_HEAD(name) \
|
||||||
|
struct list_head name = LIST_HEAD_INIT(name)
|
||||||
|
|
||||||
|
#define INIT_LIST_HEAD(ptr) do { \
|
||||||
|
(ptr)->next = (ptr); (ptr)->prev = (ptr); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Insert a new entry between two known consecutive entries.
|
||||||
|
*
|
||||||
|
* This is only for internal list manipulation where we know
|
||||||
|
* the prev/next entries already!
|
||||||
|
*/
|
||||||
|
static inline void __list_add(struct list_head *new,
|
||||||
|
struct list_head *prev,
|
||||||
|
struct list_head *next)
|
||||||
|
{
|
||||||
|
next->prev = new;
|
||||||
|
new->next = next;
|
||||||
|
new->prev = prev;
|
||||||
|
prev->next = new;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* list_add - add a new entry
|
||||||
|
* @new: new entry to be added
|
||||||
|
* @head: list head to add it after
|
||||||
|
*
|
||||||
|
* Insert a new entry after the specified head.
|
||||||
|
* This is good for implementing stacks.
|
||||||
|
*/
|
||||||
|
static inline void list_add(struct list_head *new, struct list_head *head)
|
||||||
|
{
|
||||||
|
__list_add(new, head, head->next);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* list_add_tail - add a new entry
|
||||||
|
* @new: new entry to be added
|
||||||
|
* @head: list head to add it before
|
||||||
|
*
|
||||||
|
* Insert a new entry before the specified head.
|
||||||
|
* This is useful for implementing queues.
|
||||||
|
*/
|
||||||
|
static inline void list_add_tail(struct list_head *new, struct list_head *head)
|
||||||
|
{
|
||||||
|
__list_add(new, head->prev, head);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Delete a list entry by making the prev/next entries
|
||||||
|
* point to each other.
|
||||||
|
*
|
||||||
|
* This is only for internal list manipulation where we know
|
||||||
|
* the prev/next entries already!
|
||||||
|
*/
|
||||||
|
static inline void __list_del(struct list_head * prev, struct list_head * next)
|
||||||
|
{
|
||||||
|
next->prev = prev;
|
||||||
|
prev->next = next;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* list_del - deletes entry from list.
|
||||||
|
* @entry: the element to delete from the list.
|
||||||
|
* Note: list_empty on entry does not return true after this, the entry is
|
||||||
|
* in an undefined state.
|
||||||
|
*/
|
||||||
|
static inline void list_del(struct list_head *entry)
|
||||||
|
{
|
||||||
|
__list_del(entry->prev, entry->next);
|
||||||
|
entry->next = NULL;
|
||||||
|
entry->prev = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* list_del_init - deletes entry from list and reinitialize it.
|
||||||
|
* entry: the element to delete from the list.
|
||||||
|
*/
|
||||||
|
static inline void list_del_init(struct list_head *entry)
|
||||||
|
{
|
||||||
|
__list_del(entry->prev, entry->next);
|
||||||
|
INIT_LIST_HEAD(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* list_move - delete from one list and add as another's head
|
||||||
|
* @list: the entry to move
|
||||||
|
* @head: the head that will precede our entry
|
||||||
|
*/
|
||||||
|
static inline void list_move(struct list_head *list, struct list_head *head)
|
||||||
|
{
|
||||||
|
__list_del(list->prev, list->next);
|
||||||
|
list_add(list, head);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* list_move_tail - delete from one list and add as another's tail
|
||||||
|
* @list: the entry to move
|
||||||
|
* @head: the head that will follow our entry
|
||||||
|
*/
|
||||||
|
static inline void list_move_tail(struct list_head *list,
|
||||||
|
struct list_head *head)
|
||||||
|
{
|
||||||
|
__list_del(list->prev, list->next);
|
||||||
|
list_add_tail(list, head);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* list_empty - tests whether a list is empty
|
||||||
|
* @head: the list to test.
|
||||||
|
*/
|
||||||
|
static inline int list_empty(const struct list_head *head)
|
||||||
|
{
|
||||||
|
return head->next == head;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void __list_splice(struct list_head *list,
|
||||||
|
struct list_head *head)
|
||||||
|
{
|
||||||
|
struct list_head *first = list->next;
|
||||||
|
struct list_head *last = list->prev;
|
||||||
|
struct list_head *at = head->next;
|
||||||
|
|
||||||
|
first->prev = head;
|
||||||
|
head->next = first;
|
||||||
|
|
||||||
|
last->next = at;
|
||||||
|
at->prev = last;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* list_splice - join two lists
|
||||||
|
* @list: the new list to add.
|
||||||
|
* @head: the place to add it in the first list.
|
||||||
|
*/
|
||||||
|
static inline void list_splice(struct list_head *list, struct list_head *head)
|
||||||
|
{
|
||||||
|
if (!list_empty(list))
|
||||||
|
__list_splice(list, head);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* list_splice_init - join two lists and reinitialise the emptied list.
|
||||||
|
* @list: the new list to add.
|
||||||
|
* @head: the place to add it in the first list.
|
||||||
|
*
|
||||||
|
* The list at @list is reinitialised
|
||||||
|
*/
|
||||||
|
static inline void list_splice_init(struct list_head *list,
|
||||||
|
struct list_head *head)
|
||||||
|
{
|
||||||
|
if (!list_empty(list)) {
|
||||||
|
__list_splice(list, head);
|
||||||
|
INIT_LIST_HEAD(list);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* list_entry - get the struct for this entry
|
||||||
|
* @ptr: the &struct list_head pointer.
|
||||||
|
* @type: the type of the struct this is embedded in.
|
||||||
|
* @member: the name of the list_struct within the struct.
|
||||||
|
*/
|
||||||
|
#define list_entry(ptr, type, member) \
|
||||||
|
container_of(ptr, type, member)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* list_for_each - iterate over a list
|
||||||
|
* @pos: the &struct list_head to use as a loop counter.
|
||||||
|
* @head: the head for your list.
|
||||||
|
*/
|
||||||
|
#define list_for_each(pos, head) \
|
||||||
|
for (pos = (head)->next; pos != (head); pos = pos->next)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* list_for_each_prev - iterate over a list backwards
|
||||||
|
* @pos: the &struct list_head to use as a loop counter.
|
||||||
|
* @head: the head for your list.
|
||||||
|
*/
|
||||||
|
#define list_for_each_prev(pos, head) \
|
||||||
|
for (pos = (head)->prev; pos != (head); pos = pos->prev)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* list_for_each_safe - iterate over a list safe against removal of list entry
|
||||||
|
* @pos: the &struct list_head to use as a loop counter.
|
||||||
|
* @n: another &struct list_head to use as temporary storage
|
||||||
|
* @head: the head for your list.
|
||||||
|
*/
|
||||||
|
#define list_for_each_safe(pos, n, head) \
|
||||||
|
for (pos = (head)->next, n = pos->next; pos != (head); \
|
||||||
|
pos = n, n = pos->next)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* list_for_each_entry - iterate over list of given type
|
||||||
|
* @pos: the type * to use as a loop counter.
|
||||||
|
* @head: the head for your list.
|
||||||
|
* @member: the name of the list_struct within the struct.
|
||||||
|
*/
|
||||||
|
#define list_for_each_entry(pos, head, member) \
|
||||||
|
for (pos = list_entry((head)->next, typeof(*pos), member); \
|
||||||
|
&pos->member != (head); \
|
||||||
|
pos = list_entry(pos->member.next, typeof(*pos), member))
|
||||||
|
|
||||||
|
/*
|
||||||
|
* list_for_each_entry_reverse - iterate backwards over list of given type.
|
||||||
|
* @pos: the type * to use as a loop counter.
|
||||||
|
* @head: the head for your list.
|
||||||
|
* @member: the name of the list_struct within the struct.
|
||||||
|
*/
|
||||||
|
#define list_for_each_entry_reverse(pos, head, member) \
|
||||||
|
for (pos = list_entry((head)->prev, typeof(*pos), member); \
|
||||||
|
&pos->member != (head); \
|
||||||
|
pos = list_entry(pos->member.prev, typeof(*pos), member))
|
||||||
|
|
||||||
|
/*
|
||||||
|
* list_for_each_entry_safe - iterate over list of given type safe against removal of list entry
|
||||||
|
* @pos: the type * to use as a loop counter.
|
||||||
|
* @n: another type * to use as temporary storage
|
||||||
|
* @head: the head for your list.
|
||||||
|
* @member: the name of the list_struct within the struct.
|
||||||
|
*/
|
||||||
|
#define list_for_each_entry_safe(pos, n, head, member) \
|
||||||
|
for (pos = list_entry((head)->next, typeof(*pos), member), \
|
||||||
|
n = list_entry(pos->member.next, typeof(*pos), member); \
|
||||||
|
&pos->member != (head); \
|
||||||
|
pos = n, n = list_entry(n->member.next, typeof(*n), member))
|
||||||
|
|
||||||
|
|
||||||
|
/* Return pointer to first true entry, if any, or NULL. A macro
|
||||||
|
required to allow inlining of cmpfn. */
|
||||||
|
#define LIST_FIND(head, cmpfn, type, args...) \
|
||||||
|
({ \
|
||||||
|
const struct list_head *__i, *__j = NULL; \
|
||||||
|
\
|
||||||
|
list_for_each(__i, (head)) \
|
||||||
|
if (cmpfn((const type)__i , ## args)) { \
|
||||||
|
__j = __i; \
|
||||||
|
break; \
|
||||||
|
} \
|
||||||
|
(type)__j; \
|
||||||
|
})
|
||||||
|
|
||||||
|
#endif /* _LIST_H_ */
|
301
linux/twb.c
Normal file
301
linux/twb.c
Normal file
@ -0,0 +1,301 @@
|
|||||||
|
/***************************************************************************
|
||||||
|
* 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 "list.h"
|
||||||
|
#include "twiboot.h"
|
||||||
|
|
||||||
|
#define MIN(a, b) ((a) < (b) ? (a) : (b))
|
||||||
|
|
||||||
|
#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 */
|
||||||
|
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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->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;
|
||||||
|
int size = (memtype == MEMTYPE_FLASH) ? twb->flashsize : twb->eepromsize;
|
||||||
|
uint8_t comp[READ_BLOCK_SIZE];
|
||||||
|
|
||||||
|
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, 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, size);
|
||||||
|
|
||||||
|
dbuf->length = pos;
|
||||||
|
return 0;
|
||||||
|
}
|
341
linux/twiboot.c
Normal file
341
linux/twiboot.c
Normal file
@ -0,0 +1,341 @@
|
|||||||
|
/***************************************************************************
|
||||||
|
* 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 "twiboot.h"
|
||||||
|
|
||||||
|
#define OP_READ 0x01
|
||||||
|
#define OP_WRITE 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 ]
|
||||||
|
{"no-progress", 0, 0, 'p'}, // [ -p ]
|
||||||
|
{"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 = DATATYPE_FLASH;
|
||||||
|
op->filename = strdup(arg + 6);
|
||||||
|
|
||||||
|
} else if (strncmp(arg, "eeprom:", 7) == 0) {
|
||||||
|
op->memtype = DATATYPE_EEPROM;
|
||||||
|
op->filename = strdup(arg + 7);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, "invalid memtype: '%s'\n", arg);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return op;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char * check_signature(uint8_t *sig)
|
||||||
|
{
|
||||||
|
if (sig[0] == 0x1E && sig[1] == 0x93 && sig[2] == 0x07)
|
||||||
|
return "AVR Mega 8";
|
||||||
|
|
||||||
|
if (sig[0] == 0x1E && sig[1] == 0x93 && sig[2] == 0x0A)
|
||||||
|
return "AVR Mega 88";
|
||||||
|
|
||||||
|
if (sig[0] == 0x1E && sig[1] == 0x94 && sig[2] == 0x06)
|
||||||
|
return "AVR Mega 168";
|
||||||
|
|
||||||
|
return "unknown";
|
||||||
|
}
|
||||||
|
|
||||||
|
static void progress_cb(const char *msg, int pos, int size)
|
||||||
|
{
|
||||||
|
if (pos != -1 && size != -1) {
|
||||||
|
char stars[50];
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < sizeof(stars); i++)
|
||||||
|
stars[i] = ((pos * 100 / size) >= (i * 100 / sizeof(stars))) ? '*' : ' ';
|
||||||
|
|
||||||
|
printf("%-14s: [%s] (%d)\r", msg, stars, pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pos == size)
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
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:hnpr: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_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_WRITE;
|
||||||
|
list_add_tail(&op->list, &operation_list);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
abort = 1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'n': /* no verify */
|
||||||
|
verify = 0;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'p': /* no progress */
|
||||||
|
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 - disable progress bars\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], check_signature(twb.signature));
|
||||||
|
printf("flash size : 0x%04x (0x%02x bytes/page)\n", twb.flashsize, twb.pagesize);
|
||||||
|
printf("eeprom size : 0x%04x\n", twb.eepromsize);
|
||||||
|
|
||||||
|
if (progress) {
|
||||||
|
setbuf(stdout, NULL);
|
||||||
|
twb.progress_cb = progress_cb;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct operation *op;
|
||||||
|
list_for_each_entry(op, &operation_list, list) {
|
||||||
|
abort = 1;
|
||||||
|
if (op->mode == OP_READ) {
|
||||||
|
struct databuf *dbuf;
|
||||||
|
int result;
|
||||||
|
|
||||||
|
if (op->memtype == DATATYPE_FLASH) {
|
||||||
|
twb.progress_msg = "reading flash";
|
||||||
|
result = dbuf_alloc(&dbuf, twb.flashsize);
|
||||||
|
} else if (op->memtype == DATATYPE_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_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 == DATATYPE_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 == DATATYPE_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 == DATATYPE_FLASH) {
|
||||||
|
twb.progress_msg = "verifing flash";
|
||||||
|
} else if (op->memtype == DATATYPE_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;
|
||||||
|
}
|
45
linux/twiboot.h
Normal file
45
linux/twiboot.h
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
#ifndef _TWIBOOT_H_
|
||||||
|
#define _TWIBOOT_H_
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
struct databuf {
|
||||||
|
uint32_t size; // allocation size
|
||||||
|
uint32_t length; // used size
|
||||||
|
uint8_t data[0];
|
||||||
|
};
|
||||||
|
|
||||||
|
int dbuf_alloc(struct databuf **dbuf, uint32_t size);
|
||||||
|
void dbuf_free(struct databuf *dbuf);
|
||||||
|
|
||||||
|
int file_getsize(const char *filename, uint32_t *size);
|
||||||
|
int file_read(const char *filename, struct databuf *dbuf);
|
||||||
|
int file_write(const char *filename, struct databuf *dbuf);
|
||||||
|
|
||||||
|
struct twiboot {
|
||||||
|
char *device;
|
||||||
|
uint8_t address;
|
||||||
|
int fd;
|
||||||
|
int connected;
|
||||||
|
|
||||||
|
char version[16];
|
||||||
|
uint8_t signature[3];
|
||||||
|
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);
|
||||||
|
|
||||||
|
#define DATATYPE_FLASH 0x01
|
||||||
|
#define DATATYPE_EEPROM 0x02
|
||||||
|
|
||||||
|
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_ */
|
Loading…
Reference in New Issue
Block a user