@ -1,3 +0,0 @@ | |||
*.o | |||
*.d | |||
twiboot |
@ -1,24 +0,0 @@ | |||
TARGET = twiboot | |||
TARGET2 = mpmboot | |||
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 | |||
@ln -sf $@ $(TARGET2) | |||
%.o: %.c | |||
@echo " Building file: $<" | |||
@$(CC) -c $(CFLAGS) $< -o $@ | |||
clean: | |||
rm -rf $(TARGET) $(TARGET2) *.o *.d | |||
-include $(shell find . -name \*.d 2> /dev/null) |
@ -1,52 +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 <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"; | |||
} |
@ -1,8 +0,0 @@ | |||
#ifndef _CHIPINFO_H_ | |||
#define _CHIPINFO_H_ | |||
#include <stdint.h> | |||
const char * chipinfo_get_avr_name(const uint8_t *sig); | |||
#endif /* _CHIPINFO_H_ */ |
@ -1,423 +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/types.h> | |||
#include <sys/stat.h> | |||
#include <fcntl.h> | |||
#include "filedata.h" | |||
#define FILETYPE_UNKNOWN 0 | |||
#define FILETYPE_BINARY 1 | |||
#define FILETYPE_INTELHEX 2 | |||
struct databuf * dbuf_alloc(uint32_t size) | |||
{ | |||
struct databuf *dbuf = malloc(sizeof(struct databuf) + size); | |||
if (dbuf == NULL) { | |||
perror("dbuf_alloc"); | |||
return NULL; | |||
} | |||
memset(dbuf->data, 0xFF, size); | |||
dbuf->size = size; | |||
dbuf->length = 0; | |||
return dbuf; | |||
} | |||
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; | |||
} | |||
} |
@ -1,19 +0,0 @@ | |||
#ifndef _FILEDATA_H_ | |||
#define _FILEDATA_H_ | |||
#include <stdint.h> | |||
struct databuf { | |||
uint32_t size; // allocation size | |||
uint32_t length; // used size | |||
uint8_t data[0]; | |||
}; | |||
struct databuf * dbuf_alloc(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); | |||
#endif /* _FILEDATA_H_ */ |
@ -1,268 +0,0 @@ | |||
#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_ */ |
@ -1,635 +0,0 @@ | |||
#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, | |||
}; |
@ -1,320 +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 "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); | |||