mirror of
https://github.com/adrcs/ip400.git
synced 2025-04-22 11:33:43 +03:00
252 lines
5.7 KiB
C
252 lines
5.7 KiB
C
![]() |
/*
|
||
|
stm32flash - Open Source ST STM32 flash program for *nix
|
||
|
Copyright (C) 2010 Geoffrey McRae <geoff@spacevs.com>
|
||
|
|
||
|
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; either version 2
|
||
|
of the License, or (at your option) any later version.
|
||
|
|
||
|
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||
|
*/
|
||
|
|
||
|
|
||
|
#include <sys/types.h>
|
||
|
#include <fcntl.h>
|
||
|
#include <unistd.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <stdint.h>
|
||
|
#include <stdio.h>
|
||
|
#include <string.h>
|
||
|
|
||
|
#include "hex.h"
|
||
|
#include "../compiler.h"
|
||
|
#include "../utils.h"
|
||
|
|
||
|
extern FILE *diag;
|
||
|
|
||
|
typedef struct {
|
||
|
size_t data_len, offset;
|
||
|
uint8_t *data;
|
||
|
uint32_t base;
|
||
|
} hex_t;
|
||
|
|
||
|
void* hex_init() {
|
||
|
return calloc(sizeof(hex_t), 1);
|
||
|
}
|
||
|
|
||
|
parser_err_t hex_open(void *storage, const char *filename, const char write) {
|
||
|
hex_t *st = storage;
|
||
|
if (write) {
|
||
|
return PARSER_ERR_RDONLY;
|
||
|
} else {
|
||
|
char mark;
|
||
|
int fd;
|
||
|
uint8_t checksum;
|
||
|
unsigned int c, i;
|
||
|
uint32_t base = 0;
|
||
|
unsigned int last_address = 0x0;
|
||
|
|
||
|
fd = open(filename, O_RDONLY);
|
||
|
if (fd < 0)
|
||
|
return PARSER_ERR_SYSTEM;
|
||
|
|
||
|
/* read in the file */
|
||
|
|
||
|
while(read(fd, &mark, 1) != 0) {
|
||
|
if (mark == '\n' || mark == '\r') continue;
|
||
|
if (mark != ':')
|
||
|
return PARSER_ERR_INVALID_FILE;
|
||
|
|
||
|
char buffer[9];
|
||
|
unsigned int reclen, address, type;
|
||
|
uint8_t *record = NULL;
|
||
|
|
||
|
/* get the reclen, address, and type */
|
||
|
buffer[8] = 0;
|
||
|
if (read(fd, &buffer, 8) != 8) return PARSER_ERR_INVALID_FILE;
|
||
|
if (sscanf(buffer, "%2x%4x%2x", &reclen, &address, &type) != 3) {
|
||
|
close(fd);
|
||
|
return PARSER_ERR_INVALID_FILE;
|
||
|
}
|
||
|
|
||
|
/* setup the checksum */
|
||
|
checksum =
|
||
|
reclen +
|
||
|
((address & 0xFF00) >> 8) +
|
||
|
((address & 0x00FF) >> 0) +
|
||
|
type;
|
||
|
|
||
|
switch(type) {
|
||
|
/* data record */
|
||
|
case 0:
|
||
|
if (st->data_len == 0) {
|
||
|
st->base |= address;
|
||
|
last_address = address;
|
||
|
}
|
||
|
|
||
|
c = address - last_address;
|
||
|
st->data = realloc(st->data, st->data_len + c + reclen);
|
||
|
|
||
|
/* if there is a gap, set it to 0xff and increment the length */
|
||
|
if (c > 0) {
|
||
|
memset(&st->data[st->data_len], 0xff, c);
|
||
|
st->data_len += c;
|
||
|
}
|
||
|
|
||
|
last_address = address + reclen;
|
||
|
record = &st->data[st->data_len];
|
||
|
st->data_len += reclen;
|
||
|
break;
|
||
|
|
||
|
/* extended segment address record */
|
||
|
case 2:
|
||
|
base = 0;
|
||
|
break;
|
||
|
|
||
|
/* extended linear address record */
|
||
|
case 4:
|
||
|
base = 0;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
buffer[2] = 0;
|
||
|
for(i = 0; i < reclen; ++i) {
|
||
|
if (read(fd, &buffer, 2) != 2 || sscanf(buffer, "%2x", &c) != 1) {
|
||
|
close(fd);
|
||
|
return PARSER_ERR_INVALID_FILE;
|
||
|
}
|
||
|
|
||
|
/* add the byte to the checksum */
|
||
|
checksum += c;
|
||
|
|
||
|
switch(type) {
|
||
|
case 0:
|
||
|
if (record != NULL) {
|
||
|
record[i] = c;
|
||
|
} else {
|
||
|
return PARSER_ERR_INVALID_FILE;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case 2:
|
||
|
case 4:
|
||
|
base = (base << 8) | c;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* read, scan, and verify the checksum */
|
||
|
if (
|
||
|
read(fd, &buffer, 2 ) != 2 ||
|
||
|
sscanf(buffer, "%2x", &c) != 1 ||
|
||
|
(uint8_t)(checksum + c) != 0x00
|
||
|
) {
|
||
|
close(fd);
|
||
|
return PARSER_ERR_INVALID_FILE;
|
||
|
}
|
||
|
|
||
|
switch(type) {
|
||
|
/* EOF */
|
||
|
case 1:
|
||
|
close(fd);
|
||
|
return PARSER_ERR_OK;
|
||
|
|
||
|
/* address record */
|
||
|
case 4: base = base << 12;
|
||
|
/* fall-through */
|
||
|
case 2: base = base << 4;
|
||
|
/* Reset last_address since our base changed */
|
||
|
last_address = 0;
|
||
|
|
||
|
/* Only assign the program's base address once, and only
|
||
|
* do so if we haven't seen any data records yet.
|
||
|
* If there are any data records before address records,
|
||
|
* the program's base address must be zero.
|
||
|
*/
|
||
|
if (st->base == 0 && st->data_len == 0) {
|
||
|
st->base = base;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
/* we cant cope with files out of order */
|
||
|
if (base < st->base) {
|
||
|
close(fd);
|
||
|
return PARSER_ERR_INVALID_FILE;
|
||
|
}
|
||
|
|
||
|
/* if there is a gap, enlarge and fill with 0xff */
|
||
|
size_t len = base - st->base;
|
||
|
if (len > st->data_len) {
|
||
|
size_t gap = len - st->data_len;
|
||
|
if (gap > 16384) /* arbitrary limit for warning */
|
||
|
fprintf(diag, "Warning: Filling gap of %zu bytes at 0x%08zx\n", gap, st->base + st->data_len);
|
||
|
st->data = realloc(st->data, len);
|
||
|
if (st->data == NULL) {
|
||
|
fprintf(diag, "Error: Cannot reallocate memory\n");
|
||
|
return PARSER_ERR_SYSTEM;
|
||
|
}
|
||
|
memset(&st->data[st->data_len], 0xff, gap);
|
||
|
st->data_len = len;
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
close(fd);
|
||
|
return PARSER_ERR_OK;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
parser_err_t hex_close(void *storage) {
|
||
|
hex_t *st = storage;
|
||
|
if (st) free(st->data);
|
||
|
free(st);
|
||
|
return PARSER_ERR_OK;
|
||
|
}
|
||
|
|
||
|
unsigned int hex_base(void *storage) {
|
||
|
hex_t *st = storage;
|
||
|
return st->base;
|
||
|
}
|
||
|
|
||
|
unsigned int hex_size(void *storage) {
|
||
|
hex_t *st = storage;
|
||
|
return st->data_len;
|
||
|
}
|
||
|
|
||
|
parser_err_t hex_read(void *storage, void *data, unsigned int *len) {
|
||
|
hex_t *st = storage;
|
||
|
unsigned int left = st->data_len - st->offset;
|
||
|
unsigned int get = left > *len ? *len : left;
|
||
|
|
||
|
memcpy(data, &st->data[st->offset], get);
|
||
|
st->offset += get;
|
||
|
|
||
|
*len = get;
|
||
|
return PARSER_ERR_OK;
|
||
|
}
|
||
|
|
||
|
parser_err_t hex_write(void __unused *storage, void __unused *data, unsigned int __unused len) {
|
||
|
return PARSER_ERR_RDONLY;
|
||
|
}
|
||
|
|
||
|
parser_t PARSER_HEX = {
|
||
|
"Intel HEX",
|
||
|
hex_init,
|
||
|
hex_open,
|
||
|
hex_close,
|
||
|
hex_base,
|
||
|
hex_size,
|
||
|
hex_read,
|
||
|
hex_write
|
||
|
};
|
||
|
|