#include "hex-parser.h" #include #include static int convert_hex_char_to_value(char c, uint32_t *out) { int ret = 0; uint32_t value = 0; if (!out) return -1002; switch (c) { case '0' ... '9': value = (uint32_t)c - (uint32_t)'0'; break; case 'a' ... 'f': /* Convert to upper */ c -= 0x20; /* FALLTHRU */ case 'A' ... 'F': value = (uint32_t)c - (uint32_t)'A' + 10UL; break; default: ret = -1; } if (ret == 0) *out = value; return ret; } static int convert_big_endian_hex_string_to_val(const char *string, size_t len, uint32_t *out) { int ret_val = -1; uint32_t converted_value = 0UL; uint32_t digit; int res; unsigned int i; /* Return error in case of an input error */ if (!string || !len) goto exit; if (!out) return -1003; /* we don't support strings larger than 8 chars */ if (len > 8) goto exit; for (i = 0; i < len && string[i] != '\0'; i++) { /* Convert current character to number */ res = convert_hex_char_to_value(string[i], &digit); if (res) { /* Not a hex number */ ret_val = -2; goto exit; } converted_value *= 0x10; converted_value += digit; } *out = converted_value; ret_val = 0; exit: return ret_val; } enum hex_parser_ret hex_parser_open(struct hex_parser *parser, const char *file_name) { FRESULT fres; if (!parser || !file_name) return HEX_PARSER_ERROR; parser->current_address_offset = 0UL; fres = f_open(&parser->file, file_name, FA_READ); if (fres != FR_OK) { return HEX_PARSER_ERROR; } return HEX_PARSER_OK; } static int read_line_from_file(FIL *file, char *data, int size) { char *ret_ptr; int length; ret_ptr = f_gets(data, size, file); if (!ret_ptr) return -1; /* To be sure */ data[size - 1] = 0; length = strlen(ret_ptr); return length; } static int hex_record_check_checksum(const char *buff, int hex_byte_count) { int i; int res; uint32_t checksum = 0; uint32_t tmp; if (!buff || !hex_byte_count) return -1000; for (i = 0; i < hex_byte_count; i++) { res = convert_big_endian_hex_string_to_val(&buff[2 * i], 2, &tmp); if (res) return -1; checksum += tmp; } if (checksum & 0xFF) { return 1; } return 0; } enum hex_parser_ret hex_parser_parse(struct hex_parser *parser, uint32_t *address, char *data, size_t data_len, size_t *lenout) { static char workbuff[512]; int count; int i; enum hex_parser_ret retval = HEX_PARSER_DATA_OK; uint32_t hex_addr; uint32_t byte_count; uint32_t record_type; uint32_t tmp; if (!parser || !lenout || !data_len || !data || !address) return HEX_PARSER_ERROR; /* Read a line from the file */ count = read_line_from_file(&parser->file, workbuff, sizeof(workbuff)); if (!count) { /* Check for error in case nothing is read */ if (f_error(&parser->file)) { retval = HEX_PARSER_ERROR; goto exit; } else if (f_eof(&parser->file)) { retval = HEX_PARSER_FILE_END; goto exit; } } /* Strip out invalid characters at the end */ for (i = count - 1; i >= 0; i--) { if (workbuff[i] == '\r' || workbuff[i] == '\n' || workbuff[i] == '\t' || workbuff[i] == ' ') { workbuff[i] = 0; count--; } } /* We read a valid line, check for valid marker */ if (workbuff[0] != ':') { retval = HEX_PARSER_ERROR; goto exit; } /* Line has to be longer than 11 chars in total */ if (count < 11) { retval = HEX_PARSER_ERROR; goto exit; } /* Read in the data count */ if (convert_big_endian_hex_string_to_val(&workbuff[1], 2, &byte_count)) { retval = HEX_PARSER_ERROR; goto exit; } /* Read in the address */ if (convert_big_endian_hex_string_to_val(&workbuff[3], 4, &hex_addr)) { retval = HEX_PARSER_ERROR; goto exit; } /* Read in the record type */ if (convert_big_endian_hex_string_to_val(&workbuff[7], 2, &record_type)) { retval = HEX_PARSER_ERROR; goto exit; } if (byte_count * 2 + 9 + 2 != (unsigned int)count) { /* Line not the expected length */ retval = HEX_PARSER_ERROR; goto exit; } /* Check the checksum. We have bytecount + 5 bytes in a record */ if (hex_record_check_checksum(&workbuff[1], byte_count + 5)) { retval = HEX_PARSER_ERROR; goto exit; } /* Check record type */ switch (record_type) { case 0x00: /* Data */ if (byte_count > data_len) { retval = HEX_PARSER_ERROR; break; } *lenout = 0; *address = hex_addr + parser->current_address_offset; for (i = 0; i < (int)byte_count; i++) { if (convert_big_endian_hex_string_to_val(&workbuff[9 + 2*i], 2, &tmp)) { retval = HEX_PARSER_ERROR; break; } *data = (char)(tmp & 0xFF); data++; (*lenout)++; } retval = HEX_PARSER_DATA_OK; break; case 0x01: /* End of file */ retval = HEX_PARSER_EOF_RECORD; break; case 0x04: /* extended linear address */ if (byte_count != 2) { retval = HEX_PARSER_ERROR; break; } /* Parse the upper 16 bit of the address */ if (convert_big_endian_hex_string_to_val(&workbuff[9], 4, &tmp)) { retval = HEX_PARSER_ERROR; break; } parser->current_address_offset = tmp << 16; retval = HEX_PARSER_OK; break; case 0x05: retval = HEX_PARSER_OK; break; default: retval = HEX_PARSER_ERROR; break; } exit: return retval; } enum hex_parser_ret hex_parser_close(struct hex_parser *parser) { if (!parser) return HEX_PARSER_ERROR; f_close(&parser->file); return HEX_PARSER_OK; }