view cp2102/intel_hex_in.c @ 70:09da7db45dce

doc/FTDI-EEPROM-format: document FT232R specifics
author Mychaela Falconia <falcon@freecalypso.org>
date Sun, 24 Sep 2023 21:31:37 +0000
parents 842cff427588
children
line wrap: on
line source

/*
 * This module implements a function for reading CP2102 EEPROM images
 * in the Intel HEX format which we've copied from cp210x-program-1.0,
 * the Python-language tool from 2014.
 */

#include <sys/types.h>
#include <ctype.h>
#include <string.h>
#include <strings.h>
#include <stdio.h>
#include <stdlib.h>
#include "cp210x_defs.h"

extern u_char eeprom[SIZE_EEPROM];

static int
decode_hex_digit(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
decode_hex_byte(str)
	char *str;
{
	int u, l;

	u = decode_hex_digit(str[0]);
	l = decode_hex_digit(str[1]);
	return (u << 4) | l;
}

void
read_intel_hex(filename)
	char *filename;
{
	FILE *inf;
	char linebuf[1024], *cp;
	int lineno;
	unsigned eeprom_offset;
	unsigned payload_len, record_len, n;
	u_char record[21], csum;
	unsigned record_addr, expect_addr;

	inf = fopen(filename, "r");
	if (!inf) {
		perror(filename);
		exit(1);
	}
	eeprom_offset = 0;
	for (lineno = 1; fgets(linebuf, sizeof linebuf, inf); lineno++) {
		if (!index(linebuf, '\n')) {
			fprintf(stderr,
				"%s line %d: too long or missing newline\n",
				filename, lineno);
			exit(1);
		}
		if (linebuf[0] != ':')
			continue;
		if (!isxdigit(linebuf[1]) || !isxdigit(linebuf[2])) {
inv_record:		fprintf(stderr,
				"%s line %d: invalid Intex HEX record\n",
				filename, lineno);
			exit(1);
		}
		payload_len = decode_hex_byte(linebuf + 1);
		if (payload_len > 16) {
bad_payload_len:	fprintf(stderr,
				"%s line %d: unsupported payload length\n",
				filename, lineno);
			exit(1);
		}
		record_len = payload_len + 5;
		cp = linebuf + 1;
		csum = 0;
		for (n = 0; n < record_len; n++) {
			if (!isxdigit(cp[0]) || !isxdigit(cp[1]))
				goto inv_record;
			record[n] = decode_hex_byte(cp);
			cp += 2;
			csum += record[n];
		}
		if (csum) {
			fprintf(stderr,
				"%s line %d: bad Intel HEX record checksum\n",
				filename, lineno);
			exit(1);
		}
		if (record[3] == 0x00) {
			if (payload_len != 16)
				goto bad_payload_len;
			if (eeprom_offset >= SIZE_EEPROM) {
				fprintf(stderr,
			"%s line %d: data continues past valid EEPROM size\n",
					filename, lineno);
				exit(1);
			}
			record_addr = (record[1] << 8) | record[2];
			expect_addr = EEPROM_START_ADDR + eeprom_offset;
			if (record_addr != expect_addr) {
				fprintf(stderr,
			"%s line %d: record addr is %04X, but we expect %04X\n",
					filename, lineno, record_addr,
					expect_addr);
				exit(1);
			}
			bcopy(record + 4, eeprom + eeprom_offset, 16);
			eeprom_offset += 16;
		} else if (record[3] == 0x01) {
			if (payload_len) {
				fprintf(stderr,
		"%s line %d: nonzero payload length in end-marker record\n",
					filename, lineno);
				exit(1);
			}
			if (eeprom_offset < SIZE_EEPROM) {
				fprintf(stderr,
				"%s line %d: end-marker without full data\n",
					filename, lineno);
				exit(1);
			}
			break;
		} else {
			fprintf(stderr,
			"%s line %d: unsupported Intel HEX record type\n",
				filename, lineno);
			exit(1);
		}
	}
	fclose(inf);
	if (!eeprom_offset) {
		fprintf(stderr, "error: no Intel HEX EEPROM data found in %s\n",
			filename);
		exit(1);
	}
	if (eeprom_offset < SIZE_EEPROM) {
		fprintf(stderr, "error: %s contains truncated data\n",
			filename);
		exit(1);
	}
}