view loadtools/flprogbin.c @ 686:752aef91cc95

fc-loadtool batch mode extended to support single commands
author Mychaela Falconia <falcon@freecalypso.org>
date Wed, 11 Mar 2020 00:01:58 +0000
parents f67e5ad30324
children 185c9bf208d3
line wrap: on
line source

/*
 * This module implements the flash program-bin command:
 * programming flash using a binary file as the data source.
 */

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

extern int target_fd;

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

flashcmd_progbin_int(bank, with_erase, flashoff, imgfile, fileoff, len,
			len_given)
	u_long flashoff, fileoff, len;
	char *imgfile;
{
	struct flash_bank_info *bi;
	struct discontig_prog erase_region;
	u_long origlen, bytesdone;
	u_long crc_base_addr, crc_from_target;
	uint32_t crcaccum;
	FILE *binf;
	struct stat filestat;
	char *targv[3], shortarg[10];
	u_char databuf[2048 + 7], ackbyte;
	int reclen, cc, i;
	time_t initial_time, curtime, last_time;
	unsigned duration, mm, ss;

	if (flash_detect(bank, 0) < 0)
		return(-1);
	bi = flash_bank_info + bank;
	if (flashoff >= bi->geom->total_size) {
		fprintf(stderr,
		"error: specified flash offset exceeds bank size (0x%lx)\n",
			(u_long) bi->geom->total_size);
		return(-1);
	}
	if (flashoff & 1) {
		fprintf(stderr, "error: flash offset must be even\n");
		return(-1);
	}
	binf = fopen(imgfile, "r");
	if (!binf) {
		perror(imgfile);
		return(-1);
	}
	fstat(fileno(binf), &filestat);
	if (!S_ISREG(filestat.st_mode)) {
		fprintf(stderr, "error: %s is not a regular file\n", imgfile);
		fclose(binf);
		return(-1);
	}
	if (fileoff > filestat.st_size) {
		fprintf(stderr,
			"error: specified file offset exceeds file length\n");
		fclose(binf);
		return(-1);
	}
	if (len_given) {
		if (len > filestat.st_size - fileoff) {
			fprintf(stderr,
		"error: specified file offset+length exceed file length\n");
			fclose(binf);
			return(-1);
		}
	} else
		len = filestat.st_size - fileoff;
	if (!len) {
		printf("Length is zero - nothing to do!\n");
		fclose(binf);
		return(0);
	}
	if (len > bi->geom->total_size - flashoff) {
		fprintf(stderr,
	"error: specified flash offset+length exceed bank size (0x%lx)\n",
			(u_long) bi->geom->total_size);
		fclose(binf);
		return(-1);
	}
	if (len & 1) {
		fprintf(stderr, "error: program length must be even\n");
		fclose(binf);
		return(-1);
	}

	/* finally done with the arg parsing etc, can get to work now */
	if (with_erase) {
		erase_region.start = flashoff;
		erase_region.end = flashoff + len;
		i = erase_sectors_for_prog(bi, &erase_region, 1);
		if (i < 0) {
			fclose(binf);
			return(i);
		}
	}
	crc_base_addr = bi->base_addr + flashoff;
	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(binf);
		return(-1);
	}
	cc = tpinterf_pass_output(1);
	if (cc) {
		fclose(binf);
		return(cc);
	}
	if (bi->ops->prep_for_program(bi) < 0) {
		fclose(binf);
		return(-1);
	}
	fseek(binf, fileoff, SEEK_SET);
	targv[0] = bi->ops->loadagent_binmode_cmd;
	targv[1] = 0;
	tpinterf_make_cmd(targv);
	if (tpinterf_send_cmd() < 0) {
		fclose(binf);
		return(-1);
	}
	printf("Programming flash: %lu (0x%lx) bytes\n", len, len);
	databuf[0] = 0x01;
	origlen = len;
	bytesdone = 0;
	last_time = 0;
	crcaccum = 0xFFFFFFFF;
	time(&initial_time);
	while (len) {
		if (len >= 2048)
			reclen = 2048;
		else
			reclen = len;
		cc = fread(databuf + 7, 1, reclen, binf);
		if (cc != reclen) {
			fclose(binf);
			fprintf(stderr, "error reading from %s\n", imgfile);
			/* don't leave loadagent in binary flash mode */
			databuf[0] = 0x04;
			write(target_fd, databuf, 1);
			tpinterf_pass_output(1);
			return(-1);
		}
		for (i = 0; i < reclen; i++)	/* update running CRC */
			crcaccum = crc32_table[crcaccum & 0xFF ^ databuf[i+7]]
				^ (crcaccum >> 8);
		/* binary flash write command to loadagent */
		databuf[1] = flashoff >> 24;
		databuf[2] = flashoff >> 16;
		databuf[3] = flashoff >> 8;
		databuf[4] = flashoff;
		databuf[5] = reclen >> 8;
		databuf[6] = reclen;
		cc = write(target_fd, databuf, reclen + 7);
		if (cc != reclen + 7) {
			fclose(binf);
			perror("binary write to target");
			return(-1);
		}
		i = collect_binblock_from_target(&ackbyte, 1, 8);
		if (i) {
			fclose(binf);
			return(i);
		}
		if (ackbyte == 0x15) {	/* NAK */
			fclose(binf);
			tpinterf_pass_output(1);
			return(-1);
		}
		if (ackbyte != 0x06) {	/* ACK */
			fclose(binf);
			fprintf(stderr,
				"binary protocol error: bad ack 0x%02X\n",
				ackbyte);
			return(-1);
		}
		flashoff += reclen;
		len -= reclen;
		bytesdone += reclen;
		cc = bytesdone * 100 / origlen;
		time(&curtime);
		if (curtime != last_time || cc == 100) {
			printf("\r0x%lx bytes programmed (%i%%)",
				bytesdone, cc);
			fflush(stdout);
		}
		last_time = curtime;
	}
	putchar('\n');
	fclose(binf);
	databuf[0] = 0x04;	/* EOT */
	write(target_fd, databuf, 1);
	i = collect_binblock_from_target(&ackbyte, 1, 1);
	if (i)
		return(i);
	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%us\n", mm, ss);

	/* reset flash to read mode */
	if (bi->ops->reset_cmd(bi) < 0)
		return(-1);
	printf("Verifying CRC-32 of programmed flash area\n");
	if (crc32_on_target(crc_base_addr, origlen, &crc_from_target) < 0)
		return(-1);
	if (crc_from_target == crcaccum) {
		printf("match (%08lX)\n", crc_from_target);
		return(0);
	} else {
		fprintf(stderr, "error: CRC mismatch!\n");
		return(-1);
	}
}

flashcmd_progbin_wrap(argc, argv, bank)
	char **argv;
{
	u_long flashoff, fileoff, len;
	int len_given;
	char *strtoul_endp;

	if (argc < 4 || argc > 6) {
inv:		fprintf(stderr,
		"usage: %s %s flash-offset binfile [file-offset [length]]\n",
			argv[0], argv[1]);
		return(-1);
	}
	flashoff = strtoul(argv[2], &strtoul_endp, 16);
	if (*strtoul_endp)
		goto inv;
	if (argc > 4) {
		fileoff = strtoul(argv[4], &strtoul_endp, 16);
		if (*strtoul_endp)
			goto inv;
	} else
		fileoff = 0;
	if (argc > 5) {
		len = strtoul(argv[5], &strtoul_endp, 16);
		if (*strtoul_endp)
			goto inv;
		len_given = 1;
	} else {
		len = 0;	/* dummy */
		len_given = 0;
	}
	return flashcmd_progbin_int(bank, argv[1][0] == 'e', flashoff, argv[3],
					fileoff, len, len_given);
}