view ffstools/caltools/c1xx-calextr.c @ 602:ea948d6d3b3d

CHANGES: pcm-sms-decode documented
author Mychaela Falconia <falcon@freecalypso.org>
date Mon, 10 Feb 2020 03:10:20 +0000
parents 7de38a7e1fa5
children
line wrap: on
line source

/*
 * This program parses Compal's proprietary data structure that contains
 * the factory RF calibration values among other data, locates those RF
 * calibration records, extracts their essential content (Rx GMagic and
 * Tx APC values), converts this distilled content into TCS211 RF calibration
 * tables (Rx agcparams and Tx levels) and writes these tables out in either
 * FreeCalypso ASCII or FFS binary format.
 */

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

#define	COMPAL_SECTOR_LENGTH	0x2000

u_char sector[COMPAL_SECTOR_LENGTH];
u_char endmarker[8] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
u_char record_magic[4] = {0xAA, 0x00, 0x00, 0x00};
char *ascii_output_dir, *bin_output_dir;

struct rx_calchan_map {
	unsigned	upper_bound;
	unsigned	compal_start;
	unsigned	compal_num;
};

#define	RF_RX_CAL_CHAN_SIZE	10	/* TI/FC canonical version */

struct rx_calchan_map rx_calchan_map_850[RF_RX_CAL_CHAN_SIZE] = {
	{140,  0, 2},
	{152,  2, 2},
	{164,  4, 2},
	{176,  6, 2},
	{188,  8, 2},
	{200, 10, 2},
	{212, 12, 2},
	{224, 14, 2},
	{236, 16, 2},
	{251, 18, 3}
};

struct rx_calchan_map rx_calchan_map_900[RF_RX_CAL_CHAN_SIZE] = {
	{  18,  0, 3},
	{  36,  3, 3},
	{  54,  6, 3},
	{  72,  9, 3},
	{  90, 12, 3},
	{ 108, 15, 3},
	{ 124, 18, 3},
	{ 987, 21, 3},
	{1005, 24, 3},
	{1023, 27, 3}
};

struct rx_calchan_map rx_calchan_map_1800[RF_RX_CAL_CHAN_SIZE] = {
	{548,  0, 6},
	{584,  6, 6},
	{620, 12, 6},
	{656, 18, 6},
	{692, 24, 6},
	{728, 30, 6},
	{764, 36, 6},
	{806, 42, 7},
	{848, 49, 7},
	{885, 56, 7}
};

struct rx_calchan_map rx_calchan_map_1900[RF_RX_CAL_CHAN_SIZE] = {
	{542,  0, 5},
	{572,  5, 5},
	{602, 10, 5},
	{632, 15, 5},
	{662, 20, 5},
	{692, 25, 5},
	{722, 30, 5},
	{752, 35, 5},
	{782, 40, 5},
	{810, 45, 5}
};

struct band {
	char			*name;
	unsigned		compal_record_id;
	unsigned		record_length;
	unsigned		magic2_offset;
	struct rx_calchan_map	*rx_calchan_map;
	unsigned		start_plnum;
	unsigned		end_plnum;
} bands[] = {
	{"900",  0x00, 0x94, 0x54, rx_calchan_map_900,  5, 19},
	{"1800", 0x01, 0xC8, 0x74, rx_calchan_map_1800, 0, 15},
	{"1900", 0x02, 0xB4, 0x68, rx_calchan_map_1900, 0, 15},
	{"850",  0x18, 0x88, 0x4C, rx_calchan_map_850,  5, 19},
	{0,      0,    0,    0,    0,                   0, 0}
};

read_binfile(filename, offset_arg)
	char *filename, *offset_arg;
{
	int fd, cc;
	u_long offset;
	char *endp;

	fd = open(filename, O_RDONLY);
	if (fd < 0) {
		perror(filename);
		exit(1);
	}
	offset = strtoul(offset_arg, &endp, 0);
	if (*endp) {
		fprintf(stderr, "error: invalid offset argument \"%s\"\n",
			offset_arg);
		exit(1);
	}
	lseek(fd, offset, SEEK_SET);
	cc = read(fd, sector, COMPAL_SECTOR_LENGTH);
	if (cc != COMPAL_SECTOR_LENGTH) {
		fprintf(stderr,
"error: unable to read Compal sector of %d bytes from %s at offset %s\n",
			COMPAL_SECTOR_LENGTH, filename, offset_arg);
		exit(1);
	}
	close(fd);
}

write_rx_agcparams_ascii(band, table)
	struct band *band;
	u_char *table;
{
	char pathname[MAXPATHLEN];
	FILE *of;

	sprintf(pathname, "%s/rx-agcparams.%s", ascii_output_dir, band->name);
	of = fopen(pathname, "w");
	if (!of) {
		perror(pathname);
		exit(1);
	}
	write_rx_agcparams_table(table, of);
	fclose(of);
}

write_rx_agcparams_bin(band, table)
	struct band *band;
	u_char *table;
{
	char pathname[MAXPATHLEN];
	int fd;

	sprintf(pathname, "%s/rx", bin_output_dir);
	mkdir_existok(pathname);
	sprintf(pathname, "%s/rx/agcparams.%s", bin_output_dir, band->name);
	fd = open(pathname, O_WRONLY|O_CREAT|O_TRUNC, 0666);
	if (fd < 0) {
		perror(pathname);
		exit(1);
	}
	write(fd, table, 8);
	close(fd);
}

write_rx_calchan_ascii(band, table)
	struct band *band;
	u_char *table;
{
	char pathname[MAXPATHLEN];
	FILE *of;

	sprintf(pathname, "%s/rx-calchan.%s", ascii_output_dir, band->name);
	of = fopen(pathname, "w");
	if (!of) {
		perror(pathname);
		exit(1);
	}
	write_rx_calchan_table(table, of);
	fclose(of);
}

write_rx_calchan_bin(band, table)
	struct band *band;
	u_char *table;
{
	char pathname[MAXPATHLEN];
	int fd;

	sprintf(pathname, "%s/rx", bin_output_dir);
	mkdir_existok(pathname);
	sprintf(pathname, "%s/rx/calchan.%s", bin_output_dir, band->name);
	fd = open(pathname, O_WRONLY|O_CREAT|O_TRUNC, 0666);
	if (fd < 0) {
		perror(pathname);
		exit(1);
	}
	write(fd, table, 40);
	close(fd);
}

write_tx_levels_ascii(band, table)
	struct band *band;
	u_char *table;
{
	char pathname[MAXPATHLEN];
	FILE *of;

	sprintf(pathname, "%s/tx-levels.%s", ascii_output_dir, band->name);
	of = fopen(pathname, "w");
	if (!of) {
		perror(pathname);
		exit(1);
	}
	write_tx_levels_table(table, of);
	fclose(of);
}

write_tx_levels_bin(band, table)
	struct band *band;
	u_char *table;
{
	char pathname[MAXPATHLEN];
	int fd;

	sprintf(pathname, "%s/tx", bin_output_dir);
	mkdir_existok(pathname);
	sprintf(pathname, "%s/tx/levels.%s", bin_output_dir, band->name);
	fd = open(pathname, O_WRONLY|O_CREAT|O_TRUNC, 0666);
	if (fd < 0) {
		perror(pathname);
		exit(1);
	}
	write(fd, table, 128);
	close(fd);
}

do_rx_agcparams(band, compal_data)
	struct band *band;
	u_char *compal_data;
{
	u_char rx_agcparams_table[8];

	bcopy(compal_data, rx_agcparams_table, 2);
	/* the remaining fields are constants unchanged from TI */
	rx_agcparams_table[2] = 40;
	rx_agcparams_table[3] = 0;
	rx_agcparams_table[4] = 40;
	rx_agcparams_table[5] = 0;
	rx_agcparams_table[6] = 44;
	rx_agcparams_table[7] = 0;
	if (ascii_output_dir)
		write_rx_agcparams_ascii(band, rx_agcparams_table);
	if (bin_output_dir)
		write_rx_agcparams_bin(band, rx_agcparams_table);
}

do_rx_calchan(band, compal_data)
	struct band *band;
	signed char *compal_data;
{
	u_char rx_calchan_table[40], *dp;
	struct rx_calchan_map *map;
	unsigned i, j;
	int accum, average;

	dp = rx_calchan_table;
	for (i = 0; i < RF_RX_CAL_CHAN_SIZE; i++) {
		map = band->rx_calchan_map + i;
		*dp++ = map->upper_bound;
		*dp++ = map->upper_bound >> 8;
		accum = 0;
		for (j = 0; j < map->compal_num; j++)
			accum += compal_data[map->compal_start + j];
		average = accum / (int)map->compal_num;
		*dp++ = average;
		*dp++ = average >> 8;
	}
	if (ascii_output_dir)
		write_rx_calchan_ascii(band, rx_calchan_table);
	if (bin_output_dir)
		write_rx_calchan_bin(band, rx_calchan_table);
}

do_tx_levels(band, compal_data)
	struct band *band;
	u_char *compal_data;
{
	u_char tx_levels_table[128], *sp, *dp;
	unsigned num_levels, n;

	bzero(tx_levels_table, sizeof tx_levels_table);
	num_levels = band->end_plnum - band->start_plnum + 1;
	sp = compal_data;
	dp = tx_levels_table + band->start_plnum * 4;
	for (n = 0; n < num_levels; n++) {
		*dp++ = *sp++;
		*dp++ = *sp++;
		*dp++ = n;
		*dp++ = 0;
	}
	if (ascii_output_dir)
		write_tx_levels_ascii(band, tx_levels_table);
	if (bin_output_dir)
		write_tx_levels_bin(band, tx_levels_table);
}

process_band_record(band, offset)
	struct band *band;
	unsigned offset;
{
	u_char *record;

	record = sector + offset + 8;
	if (bcmp(record, record_magic, 4)) {
		printf("bad magic1, skipping\n");
		return(-1);
	}
	if (bcmp(record + band->magic2_offset, record_magic, 4)) {
		printf("bad magic2, skipping\n");
		return(-1);
	}
	if (bcmp(record + band->magic2_offset + 8, record_magic, 4)) {
		printf("bad magic3, skipping\n");
		return(-1);
	}
	do_rx_agcparams(band, record + 4);
	do_rx_calchan(band, record + 6);
	do_tx_levels(band, record + band->magic2_offset + 12);
	return(0);
}

process_sector_data()
{
	unsigned offset, next_offset;
	u_char *header;
	unsigned hdr_words[4];
	struct band *band;
	int i;

	for (offset = 0; ; offset = next_offset) {
		if (offset > COMPAL_SECTOR_LENGTH - 12)
			break;
		header = sector + offset;
		if (!bcmp(header, endmarker, 8))
			break;
		for (i = 0; i < 4; i++)
			hdr_words[i] = header[i*2] | (header[i*2+1] << 8);
		if (!hdr_words[3]) {
			fprintf(stderr,
		"error at offset 0x%X: rounded record length word is 0\n",
				offset);
			exit(1);
		}
		if (hdr_words[3] & 3) {
			fprintf(stderr,
"error at offset 0x%X: rounded record length word is not aligned to 4\n",
				offset);
			exit(1);
		}
		if (hdr_words[3] > COMPAL_SECTOR_LENGTH - offset - 8) {
			fprintf(stderr,
"error at offset 0x%X: rounded record length spills past end of sector\n",
				offset);
			exit(1);
		}
		if (hdr_words[2] > hdr_words[3]) {
			fprintf(stderr,
	"error at offset 0x%X: native record length is greater than rounded\n",
				offset);
			exit(1);
		}
		next_offset = offset + 8 + hdr_words[3];
		if (hdr_words[0] != 0x000C)
			continue;
		for (band = bands; band->name; band++)
			if (hdr_words[1] == band->compal_record_id)
				break;
		if (!band->name)
			continue;
		printf("Found %s MHz calibration record at offset 0x%X\n",
			band->name, offset);
		if (hdr_words[2] != band->record_length) {
			printf("Oops, wrong length, skipping\n");
			continue;
		}
		process_band_record(band, offset);
	}
}

main(argc, argv)
	char **argv;
{
	int c;
	extern char *optarg;
	extern int optind;

	while ((c = getopt(argc, argv, "a:b:")) != EOF)
		switch (c) {
		case 'a':
			ascii_output_dir = optarg;
			continue;
		case 'b':
			bin_output_dir = optarg;
			continue;
		case '?':
		default:
usage:			fprintf(stderr,
	"usage: %s [-a ascii_outdir] [-b bin_outdir] c1xx-binfile offset\n",
				argv[0]);
			exit(1);
		}
	if (argc - optind != 2)
		goto usage;
	read_binfile(argv[optind], argv[optind+1]);
	if (ascii_output_dir)
		mkdir_existok(ascii_output_dir);
	if (bin_output_dir)
		mkdir_existok(bin_output_dir);
	process_sector_data();
	exit(0);
}