changeset 0:11b8a30333b3

fteeprom: initial import from freecalypso-hwlab
author Mychaela Falconia <falcon@freecalypso.org>
date Sun, 03 Sep 2023 18:08:22 +0000
parents
children ab2bb12ec959
files fteeprom/Makefile fteeprom/ftee-gen2232c.c fteeprom/ftee-gen2232h.c fteeprom/ftee-gen232r.c fteeprom/ftee-mkblank.c fteeprom/fteeprom-erase.c fteeprom/fteeprom-prog.c fteeprom/fteeprom-read.c
diffstat 8 files changed, 901 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/fteeprom/Makefile	Sun Sep 03 18:08:22 2023 +0000
@@ -0,0 +1,35 @@
+CC=	gcc
+CFLAGS=	-O2
+PROGS=	ftee-gen2232c ftee-gen2232h ftee-gen232r ftee-mkblank fteeprom-erase \
+	fteeprom-prog fteeprom-read
+INSTBIN=/opt/freecalypso/bin
+
+all:	${PROGS}
+
+ftee-gen2232c:	ftee-gen2232c.c
+	${CC} ${CFLAGS} -o $@ $@.c
+
+ftee-gen2232h:	ftee-gen2232h.c
+	${CC} ${CFLAGS} -o $@ $@.c
+
+ftee-gen232r:	ftee-gen232r.c
+	${CC} ${CFLAGS} -o $@ $@.c
+
+ftee-mkblank:	ftee-mkblank.c
+	${CC} ${CFLAGS} -o $@ $@.c
+
+fteeprom-erase:	fteeprom-erase.c
+	${CC} ${CFLAGS} -o $@ $@.c -lftdi
+
+fteeprom-prog:	fteeprom-prog.c
+	${CC} ${CFLAGS} -o $@ $@.c -lftdi
+
+fteeprom-read:	fteeprom-read.c
+	${CC} ${CFLAGS} -o $@ $@.c -lftdi
+
+install:
+	mkdir -p ${INSTBIN}
+	install -c ${PROGS} ${INSTBIN}
+
+clean:
+	rm -f ${PROGS} *.o *errs *.out
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/fteeprom/ftee-gen2232c.c	Sun Sep 03 18:08:22 2023 +0000
@@ -0,0 +1,214 @@
+#include <sys/types.h>
+#include <ctype.h>
+#include <string.h>
+#include <strings.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+char *configfile, *serial;
+
+u_short vid = 0x0403, pid = 0x6010;
+char *manuf, *product;
+u_char byte00 = 0x08, byte01 = 0x08;
+u_char byte08 = 0x80, byte0A = 0x00;
+unsigned maxpower = 100;
+u_short usb_version = 0x0200;
+
+u_short eeprom[128];
+u_char eeprom_chip = 0x46;
+unsigned eeprom_size, eeprom_string_ptr;
+
+process_cmdline(argc, argv)
+	char **argv;
+{
+	int c;
+	extern int optind;
+
+	while ((c = getopt(argc, argv, "bB")) != EOF) {
+		switch (c) {
+		case 'b':
+			eeprom_chip = 0x56;
+			continue;
+		case 'B':
+			eeprom_chip = 0x66;
+			continue;
+		default:
+			/* error msg already printed */
+			exit(1);
+		}
+	}
+	if (argc < optind + 1 || argc > optind + 2) {
+		fprintf(stderr,
+			"usage: %s [options] config-file [serial-num]\n",
+			argv[0]);
+		exit(1);
+	}
+	configfile = argv[optind];
+	serial = argv[optind+1];
+}
+
+init_eeprom_size()
+{
+	if (eeprom_chip == 0x46) {
+		eeprom_size = 64;
+		eeprom_string_ptr = 0x0B;
+	} else {
+		eeprom_size = 128;
+		eeprom_string_ptr = 0x4B;
+	}
+}
+
+read_config_file()
+{
+	FILE *inf;
+	char linebuf[1024];
+	int lineno;
+	char *cp, *np;
+
+	inf = fopen(configfile, "r");
+	if (!inf) {
+		perror(configfile);
+		exit(1);
+	}
+	for (lineno = 1; fgets(linebuf, sizeof linebuf, inf); lineno++) {
+		cp = index(linebuf, '\n');
+		if (!cp) {
+			fprintf(stderr,
+				"%s line %d: too long or unterminated\n",
+				configfile, lineno);
+			exit(1);
+		}
+		*cp = '\0';
+		for (cp = linebuf; isspace(*cp); cp++)
+			;
+		if (*cp == '\0' || *cp == '#')
+			continue;
+		for (np = cp; *cp && !isspace(*cp); cp++)
+			;
+		if (*cp)
+			*cp++ = '\0';
+		while (isspace(*cp))
+			cp++;
+		if (*cp == '\0' || *cp == '#') {
+			fprintf(stderr,
+				"%s line %d: \"%s\" setting without argument\n",
+				configfile, lineno, np);
+			exit(1);
+		}
+		if (!strcmp(np, "vid"))
+			vid = strtoul(cp, 0, 16);
+		else if (!strcmp(np, "pid"))
+			pid = strtoul(cp, 0, 16);
+		else if (!strcmp(np, "manuf"))
+			manuf = strdup(cp);
+		else if (!strcmp(np, "product"))
+			product = strdup(cp);
+		else if (!strcmp(np, "byte00"))
+			byte00 = strtoul(cp, 0, 16);
+		else if (!strcmp(np, "byte01"))
+			byte01 = strtoul(cp, 0, 16);
+		else if (!strcmp(np, "byte08"))
+			byte08 = strtoul(cp, 0, 16);
+		else if (!strcmp(np, "byte0A"))
+			byte0A = strtoul(cp, 0, 16);
+		else if (!strcmp(np, "maxpower"))
+			maxpower = strtoul(cp, 0, 10);
+		else if (!strcmp(np, "usbver"))
+			usb_version = strtoul(cp, 0, 16);
+		else {
+			fprintf(stderr, "%s line %d: unknown \"%s\" setting\n",
+				configfile, lineno, np);
+			exit(1);
+		}
+	}
+	fclose(inf);
+	if (!manuf) {
+		fprintf(stderr, "error: manuf not set in %s\n", configfile);
+		exit(1);
+	}
+	if (!product) {
+		fprintf(stderr, "error: product not set in %s\n", configfile);
+		exit(1);
+	}
+}
+
+write_string(str)
+	char *str;
+{
+	unsigned longlen, startptr;
+
+	if (eeprom_size - 1 - eeprom_string_ptr < strlen(str) + 1) {
+		fprintf(stderr, "error: strings are too long\n");
+		exit(1);
+	}
+	longlen = strlen(str) * 2 + 2;
+	startptr = eeprom_string_ptr;
+	eeprom[eeprom_string_ptr++] = 0x0300 | longlen;
+	while (*str)
+		eeprom[eeprom_string_ptr++] = *str++;
+	return (longlen << 8) | 0x80 | (startptr << 1);
+}
+
+fill_eeprom()
+{
+	u_char byte09;
+
+	if (serial)
+		byte0A |= 0x08;
+	else
+		byte0A &= 0xF7;
+	byte09 = maxpower / 2;
+	eeprom[0] = (byte01 << 8) | byte00;
+	eeprom[1] = vid;
+	eeprom[2] = pid;
+	eeprom[3] = 0x0500;
+	eeprom[4] = (byte09 << 8) | byte08;
+	eeprom[5] = byte0A;
+	eeprom[6] = usb_version;
+	eeprom[7] = write_string(manuf);
+	eeprom[8] = write_string(product);
+	if (serial)
+		eeprom[9] = write_string(serial);
+	else
+		eeprom[9] = 0;
+	eeprom[10] = eeprom_chip;
+}
+
+do_checksum()
+{
+	u_short chksum = 0xAAAA;
+	unsigned n;
+
+	for (n = 0; n < eeprom_size - 1; n++) {
+		chksum ^= eeprom[n];
+		chksum = (chksum << 1) | (chksum >> 15);
+	}
+	eeprom[n] = chksum;
+}
+
+emit_output()
+{
+	unsigned n, col;
+
+	for (n = 0; n < eeprom_size; n++) {
+		col = n & 7;
+		if (col == 0)
+			printf("%02X:", n * 2);
+		printf(" %04X", eeprom[n]);
+		if (col == 7)
+			putchar('\n');
+	}
+}
+
+main(argc, argv)
+	char **argv;
+{
+	process_cmdline(argc, argv);
+	read_config_file();
+	init_eeprom_size();
+	fill_eeprom();
+	do_checksum();
+	emit_output();
+	exit(0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/fteeprom/ftee-gen2232h.c	Sun Sep 03 18:08:22 2023 +0000
@@ -0,0 +1,220 @@
+#include <sys/types.h>
+#include <ctype.h>
+#include <string.h>
+#include <strings.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+char *configfile, *serial;
+
+u_short vid = 0x0403, pid = 0x6010;
+char *manuf, *product;
+u_char byte00 = 0x08, byte01 = 0x08;
+u_char byte08 = 0x80, byte0A = 0x00;
+unsigned maxpower = 100;
+u_char group0, group1, group2, group3;
+
+u_short eeprom[128];
+u_char eeprom_chip = 0x46;
+unsigned eeprom_size, eeprom_string_ptr;
+
+process_cmdline(argc, argv)
+	char **argv;
+{
+	int c;
+	extern int optind;
+
+	while ((c = getopt(argc, argv, "bB")) != EOF) {
+		switch (c) {
+		case 'b':
+			eeprom_chip = 0x56;
+			continue;
+		case 'B':
+			eeprom_chip = 0x66;
+			continue;
+		default:
+			/* error msg already printed */
+			exit(1);
+		}
+	}
+	if (argc < optind + 1 || argc > optind + 2) {
+		fprintf(stderr,
+			"usage: %s [options] config-file [serial-num]\n",
+			argv[0]);
+		exit(1);
+	}
+	configfile = argv[optind];
+	serial = argv[optind+1];
+}
+
+init_eeprom_size()
+{
+	if (eeprom_chip == 0x46) {
+		eeprom_size = 64;
+		eeprom_string_ptr = 0x0D;
+	} else {
+		eeprom_size = 128;
+		eeprom_string_ptr = 0x4D;
+	}
+}
+
+read_config_file()
+{
+	FILE *inf;
+	char linebuf[1024];
+	int lineno;
+	char *cp, *np;
+
+	inf = fopen(configfile, "r");
+	if (!inf) {
+		perror(configfile);
+		exit(1);
+	}
+	for (lineno = 1; fgets(linebuf, sizeof linebuf, inf); lineno++) {
+		cp = index(linebuf, '\n');
+		if (!cp) {
+			fprintf(stderr,
+				"%s line %d: too long or unterminated\n",
+				configfile, lineno);
+			exit(1);
+		}
+		*cp = '\0';
+		for (cp = linebuf; isspace(*cp); cp++)
+			;
+		if (*cp == '\0' || *cp == '#')
+			continue;
+		for (np = cp; *cp && !isspace(*cp); cp++)
+			;
+		if (*cp)
+			*cp++ = '\0';
+		while (isspace(*cp))
+			cp++;
+		if (*cp == '\0' || *cp == '#') {
+			fprintf(stderr,
+				"%s line %d: \"%s\" setting without argument\n",
+				configfile, lineno, np);
+			exit(1);
+		}
+		if (!strcmp(np, "vid"))
+			vid = strtoul(cp, 0, 16);
+		else if (!strcmp(np, "pid"))
+			pid = strtoul(cp, 0, 16);
+		else if (!strcmp(np, "manuf"))
+			manuf = strdup(cp);
+		else if (!strcmp(np, "product"))
+			product = strdup(cp);
+		else if (!strcmp(np, "byte00"))
+			byte00 = strtoul(cp, 0, 16);
+		else if (!strcmp(np, "byte01"))
+			byte01 = strtoul(cp, 0, 16);
+		else if (!strcmp(np, "byte08"))
+			byte08 = strtoul(cp, 0, 16);
+		else if (!strcmp(np, "byte0A"))
+			byte0A = strtoul(cp, 0, 16);
+		else if (!strcmp(np, "maxpower"))
+			maxpower = strtoul(cp, 0, 10);
+		else if (!strcmp(np, "group0"))
+			group0 = strtoul(cp, 0, 16);
+		else if (!strcmp(np, "group1"))
+			group1 = strtoul(cp, 0, 16);
+		else if (!strcmp(np, "group2"))
+			group2 = strtoul(cp, 0, 16);
+		else if (!strcmp(np, "group3"))
+			group3 = strtoul(cp, 0, 16);
+		else {
+			fprintf(stderr, "%s line %d: unknown \"%s\" setting\n",
+				configfile, lineno, np);
+			exit(1);
+		}
+	}
+	fclose(inf);
+	if (!manuf) {
+		fprintf(stderr, "error: manuf not set in %s\n", configfile);
+		exit(1);
+	}
+	if (!product) {
+		fprintf(stderr, "error: product not set in %s\n", configfile);
+		exit(1);
+	}
+}
+
+write_string(str)
+	char *str;
+{
+	unsigned longlen, startptr;
+
+	if (eeprom_size - 1 - eeprom_string_ptr < strlen(str) + 1) {
+		fprintf(stderr, "error: strings are too long\n");
+		exit(1);
+	}
+	longlen = strlen(str) * 2 + 2;
+	startptr = eeprom_string_ptr;
+	eeprom[eeprom_string_ptr++] = 0x0300 | longlen;
+	while (*str)
+		eeprom[eeprom_string_ptr++] = *str++;
+	return (longlen << 8) | 0x80 | (startptr << 1);
+}
+
+fill_eeprom()
+{
+	u_char byte09;
+
+	if (serial)
+		byte0A |= 0x08;
+	else
+		byte0A &= 0xF7;
+	byte09 = maxpower / 2;
+	eeprom[0] = (byte01 << 8) | byte00;
+	eeprom[1] = vid;
+	eeprom[2] = pid;
+	eeprom[3] = 0x0700;
+	eeprom[4] = (byte09 << 8) | byte08;
+	eeprom[5] = byte0A;
+	eeprom[6] = (group3 << 12) | (group2 << 8) | (group1 << 4) | group0;
+	eeprom[7] = write_string(manuf);
+	eeprom[8] = write_string(product);
+	if (serial)
+		eeprom[9] = write_string(serial);
+	else
+		eeprom[9] = 0;
+	eeprom[12] = eeprom_chip;
+}
+
+do_checksum()
+{
+	u_short chksum = 0xAAAA;
+	unsigned n;
+
+	for (n = 0; n < eeprom_size - 1; n++) {
+		chksum ^= eeprom[n];
+		chksum = (chksum << 1) | (chksum >> 15);
+	}
+	eeprom[n] = chksum;
+}
+
+emit_output()
+{
+	unsigned n, col;
+
+	for (n = 0; n < eeprom_size; n++) {
+		col = n & 7;
+		if (col == 0)
+			printf("%02X:", n * 2);
+		printf(" %04X", eeprom[n]);
+		if (col == 7)
+			putchar('\n');
+	}
+}
+
+main(argc, argv)
+	char **argv;
+{
+	process_cmdline(argc, argv);
+	read_config_file();
+	init_eeprom_size();
+	fill_eeprom();
+	do_checksum();
+	emit_output();
+	exit(0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/fteeprom/ftee-gen232r.c	Sun Sep 03 18:08:22 2023 +0000
@@ -0,0 +1,190 @@
+#include <sys/types.h>
+#include <ctype.h>
+#include <string.h>
+#include <strings.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+u_short vid = 0x0403, pid = 0x6001;
+char *manuf, *product;
+u_char byte00 = 0x00, byte01 = 0x40;
+u_char byte08 = 0x80, byte0A = 0x00, byte0B = 0x00;
+unsigned maxpower = 90;
+u_short usb_version = 0x0200;
+u_char cbusconf[5] = {3, 2, 0, 1, 5};
+
+u_short eeprom[64];
+unsigned eeprom_string_ptr = 0x0C;
+
+read_config_file(filename)
+	char *filename;
+{
+	FILE *inf;
+	char linebuf[1024];
+	int lineno;
+	char *cp, *np;
+
+	inf = fopen(filename, "r");
+	if (!inf) {
+		perror(filename);
+		exit(1);
+	}
+	for (lineno = 1; fgets(linebuf, sizeof linebuf, inf); lineno++) {
+		cp = index(linebuf, '\n');
+		if (!cp) {
+			fprintf(stderr,
+				"%s line %d: too long or unterminated\n",
+				filename, lineno);
+			exit(1);
+		}
+		*cp = '\0';
+		for (cp = linebuf; isspace(*cp); cp++)
+			;
+		if (*cp == '\0' || *cp == '#')
+			continue;
+		for (np = cp; *cp && !isspace(*cp); cp++)
+			;
+		if (*cp)
+			*cp++ = '\0';
+		while (isspace(*cp))
+			cp++;
+		if (*cp == '\0' || *cp == '#') {
+			fprintf(stderr,
+				"%s line %d: \"%s\" setting without argument\n",
+				filename, lineno, np);
+			exit(1);
+		}
+		if (!strcmp(np, "vid"))
+			vid = strtoul(cp, 0, 16);
+		else if (!strcmp(np, "pid"))
+			pid = strtoul(cp, 0, 16);
+		else if (!strcmp(np, "manuf"))
+			manuf = strdup(cp);
+		else if (!strcmp(np, "product"))
+			product = strdup(cp);
+		else if (!strcmp(np, "byte00"))
+			byte00 = strtoul(cp, 0, 16);
+		else if (!strcmp(np, "byte01"))
+			byte01 = strtoul(cp, 0, 16);
+		else if (!strcmp(np, "byte08"))
+			byte08 = strtoul(cp, 0, 16);
+		else if (!strcmp(np, "byte0A"))
+			byte0A = strtoul(cp, 0, 16);
+		else if (!strcmp(np, "byte0B"))
+			byte0B = strtoul(cp, 0, 16);
+		else if (!strcmp(np, "maxpower"))
+			maxpower = strtoul(cp, 0, 10);
+		else if (!strcmp(np, "usbver"))
+			usb_version = strtoul(cp, 0, 16);
+		else if (!strcmp(np, "cbus0"))
+			cbusconf[0] = strtoul(cp, 0, 16);
+		else if (!strcmp(np, "cbus1"))
+			cbusconf[1] = strtoul(cp, 0, 16);
+		else if (!strcmp(np, "cbus2"))
+			cbusconf[2] = strtoul(cp, 0, 16);
+		else if (!strcmp(np, "cbus3"))
+			cbusconf[3] = strtoul(cp, 0, 16);
+		else if (!strcmp(np, "cbus4"))
+			cbusconf[4] = strtoul(cp, 0, 16);
+		else {
+			fprintf(stderr, "%s line %d: unknown \"%s\" setting\n",
+				filename, lineno, np);
+			exit(1);
+		}
+	}
+	fclose(inf);
+	if (!manuf) {
+		fprintf(stderr, "error: manuf not set in %s\n", filename);
+		exit(1);
+	}
+	if (!product) {
+		fprintf(stderr, "error: product not set in %s\n", filename);
+		exit(1);
+	}
+}
+
+write_string(str)
+	char *str;
+{
+	unsigned longlen, startptr;
+
+	if (63 - eeprom_string_ptr < strlen(str) + 1) {
+		fprintf(stderr, "error: strings are too long\n");
+		exit(1);
+	}
+	longlen = strlen(str) * 2 + 2;
+	startptr = eeprom_string_ptr;
+	eeprom[eeprom_string_ptr++] = 0x0300 | longlen;
+	while (*str)
+		eeprom[eeprom_string_ptr++] = *str++;
+	return (longlen << 8) | 0x80 | (startptr << 1);
+}
+
+fill_eeprom(serial)
+	char *serial;
+{
+	u_char byte09;
+
+	if (serial)
+		byte0A |= 0x08;
+	else
+		byte0A &= 0xF7;
+	byte09 = maxpower / 2;
+	eeprom[0] = (byte01 << 8) | byte00;
+	eeprom[1] = vid;
+	eeprom[2] = pid;
+	eeprom[3] = 0x0600;
+	eeprom[4] = (byte09 << 8) | byte08;
+	eeprom[5] = (byte0B << 8) | byte0A;
+	eeprom[6] = usb_version;
+	eeprom[7] = write_string(manuf);
+	eeprom[8] = write_string(product);
+	if (serial)
+		eeprom[9] = write_string(serial);
+	else
+		eeprom[9] = 0;
+	eeprom[10] = (cbusconf[3] << 12) | (cbusconf[2] << 8) |
+			(cbusconf[1] << 4) | cbusconf[0];
+	eeprom[11] = cbusconf[4];
+}
+
+do_checksum()
+{
+	u_short chksum = 0xAAAA;
+	unsigned n;
+
+	for (n = 0; n < 63; n++) {
+		chksum ^= eeprom[n];
+		chksum = (chksum << 1) | (chksum >> 15);
+	}
+	eeprom[63] = chksum;
+}
+
+emit_output()
+{
+	unsigned n, col;
+
+	for (n = 0; n < 64; n++) {
+		col = n & 7;
+		if (col == 0)
+			printf("%02X:", n * 2);
+		printf(" %04X", eeprom[n]);
+		if (col == 7)
+			putchar('\n');
+	}
+}
+
+main(argc, argv)
+	char **argv;
+{
+	if (argc < 2 || argc > 3) {
+		fprintf(stderr, "usage: %s config-file [serial-num]\n",
+			argv[0]);
+		exit(1);
+	}
+	read_config_file(argv[1]);
+	fill_eeprom(argv[2]);
+	do_checksum();
+	emit_output();
+	exit(0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/fteeprom/ftee-mkblank.c	Sun Sep 03 18:08:22 2023 +0000
@@ -0,0 +1,31 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+
+main(argc, argv)
+	char **argv;
+{
+	unsigned size;
+	unsigned n, col;
+
+	if (argc == 1)
+		size = 64;
+	else if (argc == 2 && !strcmp(argv[1], "-b"))
+		size = 128;
+	else if (argc == 2 && !strcmp(argv[1], "-B"))
+		size = 256;
+	else {
+		fprintf(stderr, "usage: %s [-b|-B]\n", argv[0]);
+		exit(1);
+	}
+	for (n = 0; n < size; n++) {
+		col = n & 7;
+		if (col == 0)
+			printf("%02X:", n * 2);
+		printf(" %04X", 0xFFFF);
+		if (col == 7)
+			putchar('\n');
+	}
+	exit(0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/fteeprom/fteeprom-erase.c	Sun Sep 03 18:08:22 2023 +0000
@@ -0,0 +1,27 @@
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <ftdi.h>
+
+main(argc, argv)
+	char **argv;
+{
+	struct ftdi_context ftdi;
+
+	if (argc != 2) {
+		fprintf(stderr, "usage: %s device-selector\n");
+		exit(1);
+	}
+	ftdi_init(&ftdi);
+	if (ftdi_usb_open_string(&ftdi, argv[1]) < 0) {
+		fprintf(stderr, "FTDI USB open failed: %s\n", ftdi.error_str);
+		exit(1);
+	}
+	if (ftdi_erase_eeprom(&ftdi) < 0) {
+		fprintf(stderr, "EEPROM write error: %s\n", ftdi.error_str);
+		exit(1);
+	}
+	ftdi_usb_close(&ftdi);
+	exit(0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/fteeprom/fteeprom-prog.c	Sun Sep 03 18:08:22 2023 +0000
@@ -0,0 +1,117 @@
+#include <sys/types.h>
+#include <ctype.h>
+#include <string.h>
+#include <strings.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <ftdi.h>
+
+unsigned eeprom_size;
+u_short eeprom[256];
+
+read_eeprom_common(inf, filename_for_errs)
+	FILE *inf;
+	char *filename_for_errs;
+{
+	char linebuf[1024], *cp;
+	int lineno, rc;
+	unsigned ptr = 0, input_off;
+
+	for (lineno = 1; fgets(linebuf, sizeof linebuf, inf); lineno++) {
+		for (cp = linebuf; isspace(*cp); cp++)
+			;
+		if (*cp == '\0' || *cp == '#')
+			continue;
+		if (ptr >= 256) {
+			fprintf(stderr,
+				"%s line %d: maximum EEPROM size exceeded\n",
+				filename_for_errs, lineno);
+			exit(1);
+		}
+		rc = sscanf(linebuf, "%x: %hx %hx %hx %hx %hx %hx %hx %hx",
+			    &input_off, eeprom + ptr, eeprom + ptr + 1,
+			    eeprom + ptr + 2, eeprom + ptr + 3,
+			    eeprom + ptr + 4, eeprom + ptr + 5,
+			    eeprom + ptr + 6, eeprom + ptr + 7);
+		if (rc != 9 || input_off != ptr * 2) {
+			fprintf(stderr, "%s line %d: invalid input\n",
+				filename_for_errs, lineno);
+			exit(1);
+		}
+		ptr += 8;
+	}
+	if (ptr != 64 && ptr != 128 && ptr != 256) {
+		fprintf(stderr, "%s: not an EEPROM image of recognized size\n",
+			filename_for_errs);
+		exit(1);
+	}
+	eeprom_size = ptr;
+}
+
+read_eeprom_from_file(filename)
+	char *filename;
+{
+	FILE *inf;
+
+	inf = fopen(filename, "r");
+	if (!inf) {
+		perror(filename);
+		exit(1);
+	}
+	read_eeprom_common(inf, filename);
+	fclose(inf);
+}
+
+read_eeprom_from_stdin()
+{
+	read_eeprom_common(stdin, "stdin");
+}
+
+main(argc, argv)
+	char **argv;
+{
+	struct ftdi_context ftdi;
+	u_short modem_status;
+	unsigned n;
+
+	if (argc < 2 || argc > 3) {
+		fprintf(stderr, "usage: %s device-selector [eeprom-image]\n",
+			argv[0]);
+		exit(1);
+	}
+	if (argv[2])
+		read_eeprom_from_file(argv[2]);
+	else
+		read_eeprom_from_stdin();
+	ftdi_init(&ftdi);
+	if (ftdi_usb_open_string(&ftdi, argv[1]) < 0) {
+		fprintf(stderr, "FTDI USB open failed: %s\n", ftdi.error_str);
+		exit(1);
+	}
+	/* magic sequence apparently required for FT232R */
+	if (ftdi_usb_reset(&ftdi) < 0) {
+		fprintf(stderr, "ftdi_usb_reset() failed: %s\n",
+			ftdi.error_str);
+		exit(1);
+	}
+	if (ftdi_poll_modem_status(&ftdi, &modem_status) < 0) {
+		fprintf(stderr, "ftdi_poll_modem_status() failed: %s\n",
+			ftdi.error_str);
+		exit(1);
+	}
+	if (ftdi_set_latency_timer(&ftdi, 0x77) < 0) {
+		fprintf(stderr, "ftdi_set_latency_timer() failed: %s\n",
+			ftdi.error_str);
+		exit(1);
+	}
+	for (n = 0; n < eeprom_size; n++) {
+		if (ftdi_write_eeprom_location(&ftdi, n, eeprom[n]) < 0) {
+			fprintf(stderr, "EEPROM write error: %s\n",
+				ftdi.error_str);
+			exit(1);
+		}
+	}
+	ftdi_usb_close(&ftdi);
+	exit(0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/fteeprom/fteeprom-read.c	Sun Sep 03 18:08:22 2023 +0000
@@ -0,0 +1,67 @@
+#include <sys/types.h>
+#include <string.h>
+#include <strings.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <ftdi.h>
+
+char *device_selector;
+unsigned eeprom_size = 64;
+
+process_cmdline(argc, argv)
+	char **argv;
+{
+	int c;
+	extern int optind;
+
+	while ((c = getopt(argc, argv, "bB")) != EOF) {
+		switch (c) {
+		case 'b':
+			eeprom_size = 128;
+			continue;
+		case 'B':
+			eeprom_size = 256;
+			continue;
+		default:
+			/* error msg already printed */
+			exit(1);
+		}
+	}
+	if (argc != optind + 1) {
+		fprintf(stderr, "usage: %s [options] device-selector\n",
+			argv[0]);
+		exit(1);
+	}
+	device_selector = argv[optind];
+}
+
+main(argc, argv)
+	char **argv;
+{
+	struct ftdi_context ftdi;
+	u_short word;
+	unsigned n, col;
+
+	process_cmdline(argc, argv);
+	ftdi_init(&ftdi);
+	if (ftdi_usb_open_string(&ftdi, device_selector) < 0) {
+		fprintf(stderr, "FTDI USB open failed: %s\n", ftdi.error_str);
+		exit(1);
+	}
+	for (n = 0; n < eeprom_size; n++) {
+		if (ftdi_read_eeprom_location(&ftdi, n, &word) < 0) {
+			fprintf(stderr, "EEPROM read error: %s\n",
+				ftdi.error_str);
+			exit(1);
+		}
+		col = n & 7;
+		if (col == 0)
+			printf("%02X:", n * 2);
+		printf(" %04X", word);
+		if (col == 7)
+			putchar('\n');
+	}
+	ftdi_usb_close(&ftdi);
+	exit(0);
+}