view fteeprom/ftee-gen2232h.c @ 74:d069e2a6760e

ftee-gen2232h: add ftdi-chip and eeprom settings
author Mychaela Falconia <falcon@freecalypso.org>
date Tue, 26 Sep 2023 00:23:50 +0000
parents b2c891299e83
children 66cbbd7d85cf
line wrap: on
line source

/*
 * This program constructs a configuration EEPROM image for an FT2232H chip
 * based on a configuration source file giving various settings.
 */

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

extern FILE *open_eeprom_config_file();

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, eeprom_chip_cmdline;
unsigned eeprom_size, eeprom_string_ptr;

static void
process_cmdline(argc, argv)
	char **argv;
{
	int c;
	extern int optind;

	while ((c = getopt(argc, argv, "bBs")) != EOF) {
		switch (c) {
		case 'b':
			eeprom_chip = eeprom_chip_cmdline = 0x56;
			continue;
		case 'B':
			eeprom_chip = eeprom_chip_cmdline = 0x66;
			continue;
		case 's':
			eeprom_chip = eeprom_chip_cmdline = 0x46;
			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];
}

static void
ftdi_chip_setting(arg, filename_for_errs, lineno)
	char *arg, *filename_for_errs;
{
	if (!strcasecmp(arg, "FT2232H"))
		return;
	if (!strcasecmp(arg, "FT2232x"))
		return;
	fprintf(stderr, "%s line %d: config is for wrong FTDI chip\n",
		filename_for_errs, lineno);
	exit(1);
}

static void
eeprom_setting(arg, filename_for_errs, lineno)
	char *arg, *filename_for_errs;
{
	if (!strcasecmp(arg, "93C46"))
		eeprom_chip = 0x46;
	else if (!strcasecmp(arg, "93C56"))
		eeprom_chip = 0x56;
	else if (!strcasecmp(arg, "93C66"))
		eeprom_chip = 0x66;
	else {
		fprintf(stderr, "%s line %d: invalid eeprom setting\n",
			filename_for_errs, lineno);
		exit(1);
	}
	if (eeprom_chip_cmdline && eeprom_chip_cmdline != eeprom_chip) {
		fprintf(stderr,
	"%s line %d: eeprom setting disagrees with command line option\n",
			filename_for_errs, lineno);
		exit(1);
	}
}

static void
read_config_file()
{
	FILE *inf;
	char linebuf[1024];
	int lineno;
	char *cp, *np;

	inf = open_eeprom_config_file(configfile);
	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 if (!strcmp(np, "ftdi-chip"))
			ftdi_chip_setting(cp, configfile, lineno);
		else if (!strcmp(np, "eeprom"))
			eeprom_setting(cp, configfile, lineno);
		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);
	}
}

static void
init_eeprom_size()
{
	if (eeprom_chip == 0x46) {
		eeprom_size = 64;
		eeprom_string_ptr = 0x0D;
	} else {
		eeprom_size = 128;
		eeprom_string_ptr = 0x4D;
	}
}

static int
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);
}

static void
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;
}

static void
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;
}

static void
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);
}