view loadtools/romload.c @ 250:8c011177adb9

loadtools reworked to use factored-out libserial
author Mychaela Falconia <falcon@freecalypso.org>
date Thu, 21 Sep 2017 23:07:01 +0000
parents e7502631a0f9
children aba969153d20
line wrap: on
line source

/*
 * This module implements the communication protocol for pushing our
 * IRAM-loadable code to the Calypso ROM bootloader.
 */

#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include <sys/errno.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <termios.h>
#include <unistd.h>
#include "../libserial/baudrate.h"
#include "srecreader.h"

extern int errno;

extern char *target_ttydev;
extern int target_fd;
extern struct baudrate baud_rate_table[];
extern struct baudrate *find_baudrate_by_name();

struct srecreader iramimage;
struct baudrate *romload_baud_rate = baud_rate_table;	/* 1st entry default */

/* global var always defined, but does anything only for GTA0x_AP_BUILD */
int gta_modem_poweron = 1;

static int beacon_interval = 13;	/* in milliseconds */

static u_char beacon_cmd[2] = {'<', 'i'};

static u_char param_cmd[11] = {'<', 'p',
			0x00,	/* baud rate select code (115200) */
			0x00,	/* DPLL setup: leave it off like on power-up, */
				/* OsmocomBB does the same thing */
			0x00, 0x04,	/* chip select timing (WS) settings */
					/* our setting matches both OsmocomBB */
					/* and what the ROM runs with */
					/* before receiving this command */
			0x22,	/* FFFF:F900 register config, low byte */
				/* OsmocomBB sends 0x00 here, but I've chosen */
				/* 0x22 to match the setting of this register */
				/* used by the boot ROM before this command. */
			0x00, 0x01, 0xD4, 0xC0	/* UART timeout */
				/* I've chosen the same value as what the */
				/* boot ROM runs with before getting this cmd */
};

static u_char write_cmd[10] = {'<', 'w', 0x01, 0x01, 0x00};
static u_char cksum_cmd[3]  = {'<', 'c'};
static u_char branch_cmd[6] = {'<', 'b'};

#define	INTERMEDIATE_TIMEOUT	500	/* ms to wait for responses */
#define	SERIAL_FLUSH_DELAY	200	/* also in ms */

/*
 * The following function should be called by command line option
 * parsers upon encountering the -i option.
 */
set_beacon_interval(arg)
	char *arg;
{
	int i;

	i = atoi(arg);
	if (i < 2 || i > 500) {
		fprintf(stderr, "invalid -i argument specified\n");
		exit(1);
	}
	beacon_interval = i;
}

/*
 * The following function should be called by command line option
 * parsers upon encountering the -b option.
 */
set_romload_baudrate(arg)
	char *arg;
{
	struct baudrate *br;

	br = find_baudrate_by_name(arg);
	if (!br)
		exit(1);	/* error msg already printed */
	if (br->bootrom_code < 0) {
		fprintf(stderr,
		"baud rate of %s is not supported by the Calypso boot ROM\n",
			br->name);
		exit(1);
	}
	romload_baud_rate = br;
}

/*
 * The following functions alter some of the parameters sent to the
 * boot ROM in the <p command.
 */
set_romload_pll_conf(byte)
{
	param_cmd[3] = byte;
}

set_romload_rhea_cntl(byte)
{
	param_cmd[6] = byte;
}

static int
expect_response(timeout)
{
	char buf[2];
	fd_set fds;
	struct timeval tv;
	int pass, cc;

	for (pass = 0; pass < 2; ) {
		FD_ZERO(&fds);
		FD_SET(target_fd, &fds);
		tv.tv_sec = 0;
		tv.tv_usec = timeout * 1000;
		cc = select(target_fd+1, &fds, NULL, NULL, &tv);
		if (cc < 0) {
			if (errno == EINTR)
				continue;
			perror("select");
			exit(1);
		}
		if (cc < 1)
			return(-1);
		cc = read(target_fd, buf + pass, 2 - pass);
		if (cc <= 0) {
			perror("read after successful select");
			exit(1);
		}
		if (pass == 0 && buf[0] != '>')
			continue;
		pass += cc;
	}
	return(buf[1]);
}

static
send_beacons()
{
	printf("Sending beacons to %s\n", target_ttydev);
#ifdef GTA0x_AP_BUILD
	if (gta_modem_poweron)
		fork_gta_modem_poweron();
#endif
	do
		write(target_fd, beacon_cmd, sizeof beacon_cmd);
	while (expect_response(beacon_interval) != 'i');
	return 0;
}

static uint32_t
compute_block_cksum()
{
	uint32_t sum;
	int i, llen;

	sum = iramimage.datalen + 5;
	llen = iramimage.datalen + 4;
	for (i = 0; i < llen; i++)
		sum += iramimage.record[i+1];
	return sum;
}

perform_romload()
{
	int resp;
	uint16_t image_cksum;
	unsigned long rec_count;
	static int zero = 0;

	if (open_srec_file(&iramimage) < 0)
		exit(1);
	set_fixed_baudrate("19200");
	ioctl(target_fd, FIONBIO, &zero);
	send_beacons();
	printf("Got beacon response, attempting download\n");

	usleep(SERIAL_FLUSH_DELAY * 1000);
	tcflush(target_fd, TCIFLUSH);
	param_cmd[2] = romload_baud_rate->bootrom_code;
	write(target_fd, param_cmd, sizeof param_cmd);
	resp = expect_response(INTERMEDIATE_TIMEOUT);
	if (resp != 'p') {
		if (resp < 0)
			fprintf(stderr, "No response to <p command\n");
		else if (isprint(resp))
			fprintf(stderr,
			"Got >%c in response to <p command; expected >p\n",
				resp);
		else
			fprintf(stderr,
			"Got > %02X in response to <p command; expected >p\n",
				resp);
		exit(1);
	}
	printf("<p command successful, switching to %s baud\n",
		romload_baud_rate->name);
	set_serial_baudrate(romload_baud_rate);
	usleep(SERIAL_FLUSH_DELAY * 1000);
	tcflush(target_fd, TCIFLUSH);

	image_cksum = 0;
	for (rec_count = 0; ; ) {
		if (read_s_record(&iramimage) < 0)
			exit(1);
		switch (iramimage.record_type) {
		case '0':
			if (iramimage.lineno == 1)
				continue;
			fprintf(stderr,
		"%s: S0 record found in line %d (expected in line 1 only)\n",
				iramimage.filename, iramimage.lineno);
			exit(1);
		case '3':
		case '7':
			if (s3s7_get_addr_data(&iramimage) < 0)
				exit(1);
			break;
		default:
			fprintf(stderr,
				"%s line %d: S%c record type not supported\n",
				iramimage.filename, iramimage.lineno,
				iramimage.record_type);
			exit(1);
		}
		if (iramimage.record_type == '7')
			break;
		/* must be S3 */
		if (iramimage.datalen < 1) {
			fprintf(stderr,
				"%s line %d: S3 record has zero data length\n",
				iramimage.filename, iramimage.lineno);
			exit(1);
		}
		/* form <w command */
		if (!rec_count)
			printf("Sending image payload\n");
		write_cmd[5] = iramimage.datalen;
		bcopy(iramimage.record + 1, write_cmd + 6, 4);
		write(target_fd, write_cmd, sizeof write_cmd);
		write(target_fd, iramimage.record + 5, iramimage.datalen);
		/* update our checksum accumulator */
		image_cksum += ~compute_block_cksum() & 0xFF;
		/* collect response */
		resp = expect_response(INTERMEDIATE_TIMEOUT);
		if (resp != 'w') {
			fprintf(stderr, "Block #%lu: ", rec_count);
			if (resp < 0)
				fprintf(stderr, "No response to <w command\n");
			else if (isprint(resp))
				fprintf(stderr,
			"Got >%c in response to <w command; expected >w\n",
					resp);
			else
				fprintf(stderr,
			"Got > %02X in response to <w command; expected >w\n",
					resp);
			exit(1);
		}
		putchar('.');
		fflush(stdout);
		rec_count++;
	}
	/* got S7 */
	fclose(iramimage.openfile);
	if (!rec_count) {
		fprintf(stderr,
		"%s line %d: S7 without any preceding S3 data records\n",
			iramimage.filename, iramimage.lineno);
		exit(1);
	}

	/* send <c */
	printf("Sending checksum\n");
	cksum_cmd[2] = ~image_cksum & 0xFF;
	write(target_fd, cksum_cmd, sizeof cksum_cmd);
	resp = expect_response(INTERMEDIATE_TIMEOUT);
	if (resp != 'c') {
		if (resp < 0)
			fprintf(stderr, "No response to <c command\n");
		else if (isprint(resp))
			fprintf(stderr,
			"Got >%c in response to <c command; expected >c\n",
				resp);
		else
			fprintf(stderr,
			"Got > %02X in response to <c command; expected >c\n",
				resp);
		exit(1);
	}
	printf("<c command successful, sending <b\n");

	bcopy(iramimage.record + 1, branch_cmd + 2, 4);
	write(target_fd, branch_cmd, sizeof branch_cmd);
	resp = expect_response(INTERMEDIATE_TIMEOUT);
	if (resp != 'b') {
		if (resp < 0)
			fprintf(stderr, "No response to <b command\n");
		else if (isprint(resp))
			fprintf(stderr,
			"Got >%c in response to <b command; expected >b\n",
				resp);
		else
			fprintf(stderr,
			"Got > %02X in response to <b command; expected >b\n",
				resp);
		exit(1);
	}
	printf("<b command successful: downloaded image should now be running!\n");
	return(0);
}