Files
templates/c/set-protocol/src/firmware_image.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;
}