view loadtools/flprogsrec.c @ 960:411d1cc14326

sms-pdu-decode family: prepare for SC address becoming optional
author Mychaela Falconia <falcon@freecalypso.org>
date Fri, 04 Aug 2023 23:09:12 +0000
parents 185c9bf208d3
children
line wrap: on
line source

/*
 * This module implements the flash program-srec and flash program-m0 commands:
 * programming flash using S-record files as the data source.
 */

#include <sys/types.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
#include "flash.h"
#include "discontig.h"
#include "srecreader.h"

extern int target_fd;

extern struct flash_bank_info flash_bank_info[2];
extern uint32_t crc32_table[];

read_srec_img_for_flash(imgfile, is_m0, bank_size, reglistp, regcountp,
			totalp, tmpfilep)
	char *imgfile;
	uint32_t bank_size, *totalp;
	struct discontig_prog *reglistp;
	unsigned *regcountp;
	FILE **tmpfilep;
{
	struct srecreader srr;
	struct discontig_prog *regp;
	unsigned regcount;
	uint32_t total_len;
	char tmpfilename[] = "/tmp/fc-loadtoolXXXXXX";
	int rc, tmpfd, i, c;
	FILE *tmpfile;

	printf("Reading S-record image from %s\n", imgfile);
	srr.filename = imgfile;
	rc = open_srec_file(&srr);
	if (rc < 0)
		return(rc);
	tmpfd = mkstemp(tmpfilename);
	if (tmpfd < 0) {
		fprintf(stderr, "unable to get temp file via mkstemp()\n");
		fclose(srr.openfile);
		return(-1);
	}
	unlink(tmpfilename);
	tmpfile = fdopen(tmpfd, "w+");
	if (!tmpfile) {
		perror("fdopen");
		close(tmpfd);
		fclose(srr.openfile);
		return(-1);
	}
	regp = reglistp;
	regcount = 0;
	total_len = 0;
	for (;;) {
		if (read_s_record(&srr) < 0) {
			/* error msg already printed */
			fclose(srr.openfile);
			fclose(tmpfile);
			return(-1);
		}
		if (srr.record_type == '0') {
			if (srr.lineno == 1)
				continue;
			fprintf(stderr,
	"error: S0 record found in line %d of %s (expected in line 1 only)\n",
				srr.lineno, srr.filename);
			fclose(srr.openfile);
			fclose(tmpfile);
			return(-1);
		} else if (srr.record_type == '7')
			break;
		else if (srr.record_type != '3') {
			fprintf(stderr,
			"error: unsupported S%c record type in line %d of %s\n",
				srr.record_type, srr.lineno, srr.filename);
			fclose(srr.openfile);
			fclose(tmpfile);
			return(-1);
		}
		/* must be S3 */
		if (s3s7_get_addr_data(&srr) < 0) {
			/* error msg already printed */
			fclose(srr.openfile);
			fclose(tmpfile);
			return(-1);
		}
		if (srr.datalen < 1) {
			fprintf(stderr,
			"%s line %d: S3 record of zero data length ignored\n",
				srr.filename, srr.lineno);
			continue;
		}
		if (srr.addr & 1 || srr.datalen & 1) {
			fprintf(stderr,
			"%s line %d: violates word alignment requirement\n",
				srr.filename, srr.lineno);
			fclose(srr.openfile);
			fclose(tmpfile);
			return(-1);
		}
		srr.addr &= bank_size - 1;
		if (srr.addr + srr.datalen > bank_size) {
			fprintf(stderr,
			"%s line %d: goes past the end of the flash bank\n",
				srr.filename, srr.lineno);
			fclose(srr.openfile);
			fclose(tmpfile);
			return(-1);
		}
		/* is this the first record of the first region? */
		if (!regcount) {
			regp->start = srr.addr;
			regp->end = srr.addr;
			regp->crc = 0xFFFFFFFF;
			regcount = 1;
		}
		if (srr.addr < regp->end) {
			fprintf(stderr, "%s line %d: address going backwards\n",
				srr.filename, srr.lineno);
			fclose(srr.openfile);
			fclose(tmpfile);
			return(-1);
		}
		if (srr.addr != regp->end) {
			if (regcount >= MAX_SREC_REGIONS) {
				fprintf(stderr,
			"error: %s has too many discontiguous regions\n",
					imgfile);
				fclose(srr.openfile);
				fclose(tmpfile);
				return(-1);
			}
			regp++;
			regcount++;
			regp->start = srr.addr;
			regp->end = srr.addr;
			regp->crc = 0xFFFFFFFF;
		}
		/* take in the payload */
		if (is_m0) {
			for (i = 0; i < srr.datalen; i += 2) {
				c = srr.record[i+6];
				regp->crc = crc32_table[regp->crc & 0xFF ^ c]
					^ (regp->crc >> 8);
				putc(c, tmpfile);
				c = srr.record[i+5];
				regp->crc = crc32_table[regp->crc & 0xFF ^ c]
					^ (regp->crc >> 8);
				putc(c, tmpfile);
			}
		} else {
			for (i = 0; i < srr.datalen; i++) {
				c = srr.record[i+5];
				regp->crc = crc32_table[regp->crc & 0xFF ^ c]
					^ (regp->crc >> 8);
				putc(c, tmpfile);
			}
		}
		regp->end += srr.datalen;
		total_len += srr.datalen;
	}
	/* got S7 */
	fclose(srr.openfile);
	if (!regcount) {
		fprintf(stderr,
		"%s line %d: S7 without any preceding S3 data records\n",
			srr.filename, srr.lineno);
		fclose(tmpfile);
		return(-1);
	}
	/* good read */
	if (regcount == 1)
		printf("Got %lu (0x%lx) bytes in one contiguous region\n",
			(u_long) total_len, (u_long) total_len);
	else
		printf("Got %lu (0x%lx) bytes in %u discontiguous regions\n",
			(u_long) total_len, (u_long) total_len, regcount);
	*regcountp = regcount;
	*totalp = total_len;
	*tmpfilep = tmpfile;
	return(0);
}

flashcmd_progsrec_gen(bank, imgfile, is_m0, with_erase)
	char *imgfile;
{
	struct flash_bank_info *bi;
	struct discontig_prog regions[MAX_SREC_REGIONS], *regp;
	unsigned nregions, reg;
	uint32_t total_len, bytesdone, addr, len;
	FILE *tmpfile;
	char *targv[3], shortarg[10];
	u_char databuf[2048 + 7], ackbyte;
	int reclen, cc, rc;
	time_t initial_time, curtime, last_time;
	unsigned duration, mm, ss;
	u_long crc_from_target;

	if (flash_detect(bank, 0) < 0)
		return(-1);
	bi = flash_bank_info + bank;
	rc = read_srec_img_for_flash(imgfile, is_m0, bi->geom->total_size,
				     regions, &nregions, &total_len, &tmpfile);
	if (rc < 0)
		return(rc);
	if (with_erase) {
		rc = erase_sectors_for_prog(bi, regions, nregions);
		if (rc < 0) {
			fclose(tmpfile);
			return(rc);
		}
	}
	sprintf(shortarg, "%lx", (u_long) bi->base_addr);
	targv[0] = bi->ops->loadagent_setbase_cmd;
	targv[1] = shortarg;
	targv[2] = 0;
	printf("Setting flash base address: %s %s\n", targv[0], targv[1]);
	tpinterf_make_cmd(targv);
	if (tpinterf_send_cmd() < 0) {
		fclose(tmpfile);
		return(-1);
	}
	rc = tpinterf_pass_output(1);
	if (rc) {
		fclose(tmpfile);
		return(rc);
	}
	if (bi->ops->prep_for_program(bi) < 0) {
		fclose(tmpfile);
		return(-1);
	}
	rewind(tmpfile);
	targv[0] = bi->ops->loadagent_binmode_cmd;
	targv[1] = 0;
	tpinterf_make_cmd(targv);
	if (tpinterf_send_cmd() < 0) {
		fclose(tmpfile);
		return(-1);
	}
	printf("Programming flash\n");
	databuf[0] = 0x01;
	bytesdone = 0;
	last_time = 0;
	time(&initial_time);
	for (reg = 0, regp = regions; reg < nregions; reg++, regp++) {
		addr = regp->start;
		len = regp->end - addr;
		while (len) {
			if (len >= 2048)
				reclen = 2048;
			else
				reclen = len;
			cc = fread(databuf + 7, 1, reclen, tmpfile);
			if (cc != reclen) {
				fclose(tmpfile);
				fprintf(stderr,
					"error reading from temp file!\n");
				/* don't leave loadagent in binary flash mode */
				databuf[0] = 0x04;
				write(target_fd, databuf, 1);
				tpinterf_pass_output(1);
				return(-1);
			}
			/* binary flash write command to loadagent */
			databuf[1] = addr >> 24;
			databuf[2] = addr >> 16;
			databuf[3] = addr >> 8;
			databuf[4] = addr;
			databuf[5] = reclen >> 8;
			databuf[6] = reclen;
			cc = write(target_fd, databuf, reclen + 7);
			if (cc != reclen + 7) {
				fclose(tmpfile);
				perror("binary write to target");
				return(-1);
			}
			rc = collect_binblock_from_target(&ackbyte, 1, 8);
			if (rc) {
				fclose(tmpfile);
				return(rc);
			}
			if (ackbyte == 0x15) {	/* NAK */
				fclose(tmpfile);
				tpinterf_pass_output(1);
				return(-1);
			}
			if (ackbyte != 0x06) {	/* ACK */
				fclose(tmpfile);
				fprintf(stderr,
				"binary protocol error: bad ack 0x%02X\n",
					ackbyte);
				return(-1);
			}
			addr += reclen;
			len -= reclen;
			bytesdone += reclen;
			cc = bytesdone * 100 / total_len;
			time(&curtime);
			if (curtime != last_time || cc == 100) {
				printf("\r0x%lx bytes programmed (%i%%)",
					bytesdone, cc);
				fflush(stdout);
			}
			last_time = curtime;
		}
	}
	putchar('\n');
	fclose(tmpfile);
	databuf[0] = 0x04;	/* EOT */
	write(target_fd, databuf, 1);
	rc = collect_binblock_from_target(&ackbyte, 1, 1);
	if (rc)
		return(rc);
	time(&last_time);
	if (ackbyte != '=') {
		fprintf(stderr, "error: \'=\' not received as expected\n");
		return(-1);
	}
	duration = last_time - initial_time;
	mm = duration / 60;
	ss = duration - mm * 60;
	printf("Operation completed in %um%02us\n", mm, ss);

	/* reset flash to read mode */
	if (bi->ops->reset_cmd(bi) < 0)
		return(-1);
	printf("Verifying CRC-32 of %u programmed region(s)\n", nregions);
	for (reg = 0, regp = regions; reg < nregions; reg++, regp++) {
		rc = crc32_on_target((u_long) (bi->base_addr + regp->start),
				     (u_long) (regp->end - regp->start),
				     &crc_from_target);
		if (rc < 0)
			return(rc);
		if (crc_from_target != regp->crc) {
			fprintf(stderr, "error: CRC mismatch!\n");
			return(-1);
		}
		putchar('.');
		fflush(stdout);
	}
	putchar('\n');
	return(0);
}

flashcmd_program_srec(argc, argv, bank)
	char **argv;
{
	if (argc != 3) {
		fprintf(stderr, "usage: %s %s image.srec\n", argv[0], argv[1]);
		return(-1);
	}
	return flashcmd_progsrec_gen(bank, argv[2], 0, argv[1][0] == 'e');
}

flashcmd_program_m0(argc, argv, bank)
	char **argv;
{
	if (argc != 3) {
		fprintf(stderr, "usage: %s %s image.m0\n", argv[0], argv[1]);
		return(-1);
	}
	return flashcmd_progsrec_gen(bank, argv[2], 1, argv[1][0] == 'e');
}