89 lines
3.5 KiB
C
89 lines
3.5 KiB
C
#include "firmware_image.h"
|
|
#include <stdlib.h>
|
|
|
|
static int hex_digit(char c)
|
|
{
|
|
if (c >= '0' && c <= '9') return c - '0';
|
|
if (c >= 'A' && c <= 'F') return c - 'A' + 10;
|
|
if (c >= 'a' && c <= 'f') return c - 'a' + 10;
|
|
return -1;
|
|
}
|
|
static int space(char c) { return c == ' ' || c == '\t' || c == '\r'; }
|
|
static int compare_cells(const void *a, const void *b)
|
|
{
|
|
uint32_t x = ((const firmware_hex_cell_t *)a)->address;
|
|
uint32_t y = ((const firmware_hex_cell_t *)b)->address;
|
|
return (x > y) - (x < y);
|
|
}
|
|
int firmware_hex_parse(const char *text, size_t length,
|
|
firmware_hex_cell_t *cells, size_t capacity, size_t max_records,
|
|
size_t *count, size_t *error_line)
|
|
{
|
|
size_t pos = 0, line = 0, used = 0, records = 0, i;
|
|
uint32_t upper = 0;
|
|
int eof = 0;
|
|
if (count == NULL || error_line == NULL) return FW_HEX_FORMAT;
|
|
*count = 0; *error_line = 0;
|
|
if (text == NULL || cells == NULL) return FW_HEX_FORMAT;
|
|
while (pos < length) {
|
|
size_t begin = pos, end, n;
|
|
uint8_t bytes[260];
|
|
unsigned sum = 0, size, kind, address;
|
|
while (pos < length && text[pos] != '\n') ++pos;
|
|
end = pos;
|
|
if (pos < length) ++pos;
|
|
++line;
|
|
while (begin < end && space(text[begin])) ++begin;
|
|
while (end > begin && space(text[end-1])) --end;
|
|
if (begin == end) continue;
|
|
*error_line = line;
|
|
if (eof) return FW_HEX_EOF;
|
|
if (++records > max_records) return FW_HEX_CAPACITY;
|
|
if (text[begin++] != ':') return FW_HEX_PREFIX;
|
|
if ((end-begin) & 1U) return FW_HEX_DIGITS;
|
|
n = (end-begin)/2;
|
|
if (n < 5 || n > sizeof(bytes)) return FW_HEX_FORMAT;
|
|
for (i = 0; i < n; ++i) {
|
|
int hi = hex_digit(text[begin+2*i]), lo = hex_digit(text[begin+2*i+1]);
|
|
if (hi < 0 || lo < 0) return FW_HEX_DIGITS;
|
|
bytes[i] = (uint8_t)(hi*16+lo); sum += bytes[i];
|
|
}
|
|
size = bytes[0]; address = ((unsigned)bytes[1]<<8) | bytes[2]; kind = bytes[3];
|
|
if (n != size+5U) return FW_HEX_LENGTH;
|
|
if (sum & 255U) return FW_HEX_CHECKSUM;
|
|
if (kind == 0) {
|
|
uint64_t absolute = (uint64_t)upper + address;
|
|
if (size && absolute + size - 1 > UINT32_MAX) return FW_HEX_RANGE;
|
|
if (size > capacity-used) return FW_HEX_CAPACITY;
|
|
for (i = 0; i < size; ++i) {
|
|
cells[used].address = (uint32_t)(absolute+i);
|
|
cells[used++].value = bytes[4+i];
|
|
}
|
|
} else if (kind == 1) {
|
|
if (size || address) return FW_HEX_RECORD;
|
|
eof = 1;
|
|
} else if (kind == 2 || kind == 4) {
|
|
if (size != 2 || address) return FW_HEX_RECORD;
|
|
upper = ((uint32_t)bytes[4]<<8) | bytes[5];
|
|
upper <<= kind == 2 ? 4 : 16;
|
|
} else if (kind == 3 || kind == 5) {
|
|
if (size != 4 || address) return FW_HEX_RECORD;
|
|
} else return FW_HEX_RECORD;
|
|
}
|
|
if (!eof || !used) return FW_HEX_EOF;
|
|
qsort(cells, used, sizeof(*cells), compare_cells);
|
|
for (i = 1; i < used; ++i)
|
|
if (cells[i-1].address == cells[i].address) { *error_line = 0; return FW_HEX_OVERLAP; }
|
|
*count = used; *error_line = 0;
|
|
return FW_HEX_OK;
|
|
}
|
|
size_t firmware_hex_segment_size(const firmware_hex_cell_t *cells, size_t count, size_t offset)
|
|
{
|
|
size_t end;
|
|
if (cells == NULL || offset >= count) return 0;
|
|
end = offset+1;
|
|
while (end < count && cells[end-1].address != UINT32_MAX &&
|
|
cells[end].address == cells[end-1].address+1U) ++end;
|
|
return end-offset;
|
|
}
|