view cp2102/apply_eeprom_patch.c @ 106:de3b299561b3

udev: add rules file that works on my system
author Mychaela Falconia <falcon@freecalypso.org>
date Thu, 05 Oct 2023 01:08:28 +0000
parents 915a6fa7723e
children
line wrap: on
line source

/*
 * This function implemented in this module reads a CP2102 EEPROM patch
 * specification file (which can also be a baud rate table file) and applies
 * patches to the EEPROM image in RAM.
 */

#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 char cp2102_install_dir[] = "/opt/freecalypso/cp2102";

static void
apply_baud_entry(filename, lineno, args)
	char *filename, *args;
{
	unsigned entry_idx, intend_baud, baudgen_val, timeout_val;
	unsigned prescaler, extra_byte;
	char *cp = args;
	u_char *eerec;

	if (!isdigit(*cp)) {
inv:		fprintf(stderr, "%s line %d: invalid syntax for baud-entry\n",
			filename, lineno);
		exit(1);
	}
	entry_idx = strtoul(cp, &cp, 10);
	while (isspace(*cp))
		cp++;
	if (*cp++ != ':')
		goto inv;
	while (isspace(*cp))
		cp++;
	if (!isdigit(*cp))
		goto inv;
	intend_baud = strtoul(cp, &cp, 10);
	while (isspace(*cp))
		cp++;
	if (*cp++ != '=')
		goto inv;
	while (isspace(*cp))
		cp++;
	if (!isxdigit(*cp))
		goto inv;
	baudgen_val = strtoul(cp, &cp, 16);
	while (isspace(*cp))
		cp++;
	if (*cp++ != ',')
		goto inv;
	while (isspace(*cp))
		cp++;
	if (!isxdigit(*cp))
		goto inv;
	timeout_val = strtoul(cp, &cp, 16);
	while (isspace(*cp))
		cp++;
	if (*cp++ != ',')
		goto inv;
	while (isspace(*cp))
		cp++;
	if (!isdigit(*cp))
		goto inv;
	prescaler = strtoul(cp, &cp, 10);
	while (isspace(*cp))
		cp++;
	if (*cp++ != ',')
		goto inv;
	while (isspace(*cp))
		cp++;
	if (!isdigit(*cp))
		goto inv;
	extra_byte = strtoul(cp, &cp, 10);
	while (isspace(*cp))
		cp++;
	if (*cp && *cp != '#')
		goto inv;
	/* done with parsing, get to EEPROM */
	if (entry_idx > 31) {
		fprintf(stderr, "%s line %d: invalid baud-entry index\n",
			filename, lineno);
		exit(1);
	}
	eerec = eeprom + entry_idx * SIZE_BAUDRATE_CFG;
	eerec[0] = baudgen_val >> 8;
	eerec[1] = baudgen_val;
	eerec[2] = timeout_val >> 8;
	eerec[3] = timeout_val;
	eerec[4] = prescaler;
	eerec[5] = extra_byte;
	eerec[6] = intend_baud;
	eerec[7] = intend_baud >> 8;
	eerec[8] = intend_baud >> 16;
	eerec[9] = intend_baud >> 24;
}

static void
apply_patch_8bit(filename, lineno, args)
	char *filename, *args;
{
	unsigned patch_loc, patch_val;
	char *cp = args;

	if (!isxdigit(*cp)) {
inv:		fprintf(stderr, "%s line %d: invalid syntax for patch8\n",
			filename, lineno);
		exit(1);
	}
	patch_loc = strtoul(cp, &cp, 16);
	while (isspace(*cp))
		cp++;
	if (!isxdigit(*cp))
		goto inv;
	patch_val = strtoul(cp, &cp, 16);
	while (isspace(*cp))
		cp++;
	if (*cp && *cp != '#')
		goto inv;
	/* validate range */
	if (patch_loc >= EEPROM_START_ADDR &&
	    patch_loc <= EEPROM_START_ADDR + SIZE_EEPROM - 1)
		patch_loc -= EEPROM_START_ADDR;
	else if (patch_loc > SIZE_EEPROM - 1) {
		fprintf(stderr, "%s line %d: invalid patch address\n",
			filename, lineno);
		exit(1);
	}
	if (patch_val > 0xFF) {
		fprintf(stderr,
			"%s line %d: patch value does not fit into 8 bits\n",
			filename, lineno);
		exit(1);
	}
	/* checks done, proceed */
	eeprom[patch_loc] = patch_val;
}

static void
apply_patch_16bit(filename, lineno, args)
	char *filename, *args;
{
	unsigned patch_loc, patch_val;
	char *cp = args;

	if (!isxdigit(*cp)) {
inv:		fprintf(stderr, "%s line %d: invalid syntax for patch16\n",
			filename, lineno);
		exit(1);
	}
	patch_loc = strtoul(cp, &cp, 16);
	while (isspace(*cp))
		cp++;
	if (!isxdigit(*cp))
		goto inv;
	patch_val = strtoul(cp, &cp, 16);
	while (isspace(*cp))
		cp++;
	if (*cp && *cp != '#')
		goto inv;
	/* validate range */
	if (patch_loc >= EEPROM_START_ADDR &&
	    patch_loc <= EEPROM_START_ADDR + SIZE_EEPROM - 2)
		patch_loc -= EEPROM_START_ADDR;
	else if (patch_loc > SIZE_EEPROM - 2) {
		fprintf(stderr, "%s line %d: invalid patch address\n",
			filename, lineno);
		exit(1);
	}
	if (patch_val > 0xFFFF) {
		fprintf(stderr,
			"%s line %d: patch value does not fit into 16 bits\n",
			filename, lineno);
		exit(1);
	}
	/* checks done, proceed */
	eeprom[patch_loc] = patch_val;
	eeprom[patch_loc+1] = patch_val >> 8;
}

static void
apply_patch_vid(filename, lineno, args)
	char *filename, *args;
{
	unsigned patch_val;
	char *cp = args;

	if (!isxdigit(*cp)) {
inv:		fprintf(stderr, "%s line %d: invalid syntax for vid\n",
			filename, lineno);
		exit(1);
	}
	patch_val = strtoul(cp, &cp, 16);
	while (isspace(*cp))
		cp++;
	if (*cp && *cp != '#')
		goto inv;
	/* validate range */
	if (patch_val > 0xFFFF) {
		fprintf(stderr, "%s line %d: VID does not fit into 16 bits\n",
			filename, lineno);
		exit(1);
	}
	/* checks done, proceed */
	eeprom[0x390] = patch_val;
	eeprom[0x391] = patch_val >> 8;
}

static void
apply_patch_pid(filename, lineno, args)
	char *filename, *args;
{
	unsigned patch_val;
	char *cp = args;

	if (!isxdigit(*cp)) {
inv:		fprintf(stderr, "%s line %d: invalid syntax for pid\n",
			filename, lineno);
		exit(1);
	}
	patch_val = strtoul(cp, &cp, 16);
	while (isspace(*cp))
		cp++;
	if (*cp && *cp != '#')
		goto inv;
	/* validate range */
	if (patch_val > 0xFFFF) {
		fprintf(stderr, "%s line %d: PID does not fit into 16 bits\n",
			filename, lineno);
		exit(1);
	}
	/* checks done, proceed */
	eeprom[0x392] = patch_val;
	eeprom[0x393] = patch_val >> 8;
}

static void
set_string_desc(start_offset, str)
	unsigned start_offset;
	char *str;
{
	char *cp;
	u_char *dp;

	dp = eeprom + start_offset;
	*dp++ = strlen(str) * 2 + 2;
	*dp++ = 0x03;
	for (cp = str; *cp; ) {
		*dp++ = *cp++;
		*dp++ = 0;
	}
}

static void
set_manuf_string(filename, lineno, arg)
	char *filename, *arg;
{
	if (strlen(arg) > 29) {
		fprintf(stderr, "%s line %d: manuf string is too long\n",
			filename, lineno);
		exit(1);
	}
	set_string_desc(0x3C3, arg);
}

static void
set_product_string(filename, lineno, arg)
	char *filename, *arg;
{
	if (strlen(arg) > 126) {
		fprintf(stderr, "%s line %d: product string is too long\n",
			filename, lineno);
		exit(1);
	}
	set_string_desc(0x208, arg);
}

static void
set_serial_string(filename, lineno, arg)
	char *filename, *arg;
{
	if (strlen(arg) > 63) {
		fprintf(stderr, "%s line %d: serial string is too long\n",
			filename, lineno);
		exit(1);
	}
	set_string_desc(0x307, arg);
}

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

	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, "baud-entry"))
			apply_baud_entry(filename, lineno, cp);
		else if (!strcmp(np, "patch8"))
			apply_patch_8bit(filename, lineno, cp);
		else if (!strcmp(np, "patch16"))
			apply_patch_16bit(filename, lineno, cp);
		else if (!strcmp(np, "vid"))
			apply_patch_vid(filename, lineno, cp);
		else if (!strcmp(np, "pid"))
			apply_patch_pid(filename, lineno, cp);
		else if (!strcmp(np, "manuf"))
			set_manuf_string(filename, lineno, cp);
		else if (!strcmp(np, "product"))
			set_product_string(filename, lineno, cp);
		else if (!strcmp(np, "serial"))
			set_serial_string(filename, lineno, cp);
		else {
			fprintf(stderr, "%s line %d: unknown \"%s\" setting\n",
				filename, lineno, np);
			exit(1);
		}
	}
}

static FILE *
open_with_search(req_filename)
	char *req_filename;
{
	char pathbuf[256];
	FILE *f;

	if (!index(req_filename, '/') && strlen(req_filename) < 128) {
		sprintf(pathbuf, "%s/%s", cp2102_install_dir, req_filename);
		f = fopen(pathbuf, "r");
		if (f)
			return f;
	}
	f = fopen(req_filename, "r");
	return f;
}

void
apply_eeprom_patch_file(filename)
	char *filename;
{
	FILE *inf;

	inf = open_with_search(filename);
	if (!inf) {
		perror(filename);
		exit(1);
	}
	apply_eeprom_patch_int(filename, inf);
	fclose(inf);
}

void
apply_eeprom_patch_baud(baud_spec_name)
	char *baud_spec_name;
{
	char pathbuf[256];
	FILE *inf;

	if (strlen(baud_spec_name) > 63) {
		fprintf(stderr, "error: no buffer overflow attacks, please\n");
		exit(1);
	}
	sprintf(pathbuf, "%s/baudtab-%s", cp2102_install_dir, baud_spec_name);
	inf = fopen(pathbuf, "r");
	if (!inf) {
		perror(pathbuf);
		exit(1);
	}
	apply_eeprom_patch_int(pathbuf, inf);
	fclose(inf);
}