shithub: stm32up

ref: 4ff7f599491c5d29de20583490b5dc4b8d43c8fc
dir: /stm32up.c/

View raw version
#include <u.h>
#include <libc.h>

static int debug = 0;
static uchar buf[256];

enum {
	Doinfo,
	Doread,
	Dowrite,
	Doerase,
	Doflash,
	Dogo,
	Doreadprot,
	Doreadunprot,
	Dowriteprot,
	Dowriteunprot,
	Docsum
};

enum Uartcmd {
	Nop			= 0x00,
	Full		= 0xFF,
	Ack			= 0x79,
	Nack		= 0x1F,
	Conninit	= 0x7F,
	Get			= 0x00,
	Getversion	= 0x01,
	Getid		= 0x02,
	Read		= 0x11,
	Go			= 0x21,
	Write		= 0x31,
	Erase		= 0x43,
	Eraseext	= 0x44,
	Special		= 0x50,
	Specialext	= 0x51,
	Writeprot	= 0x63,
	Writeunprot	= 0x73,
	Readprot	= 0x82,
	Readunprot	= 0x92,
	Checksum	= 0xA1	// this is not yet implemented
};

void
stm_info(int dev)
{
	if(debug)
		fprint(2, "acquiring device info\n");

	// get the protocol version and commands
	buf[0] = Get; buf[1] = Full ^ Get;
	if(write(dev, buf, 2) != 2) {
		fprint(2, "unable to write: %r\n");
		exits("deverr");
	}

	if(readn(dev, buf, 1) == 0 || buf[0] != Ack) {
		fprint(2, "'get' rejected\n");
		exits("protoerr");
	}
	if(readn(dev, buf, 14) == 0 || buf[0] != 11 || buf[13] != Ack) {
		fprint(2, "protocol error while getting device info\n");
		exits("protoerr");
	}

	fprint(2, "protocol version: %x\n", buf[1]);

	if(debug) {
		for(int i = 0; i < 14; i++)
			fprint(2, "0x%02x ", buf[i]);
		fprint(2, "\n");
	}

	if(buf[2] != Get ||
		buf[3] != Getversion ||
		buf[4] != Getid ||
		buf[5] != Read ||
		buf[6] != Go ||
		buf[7] != Write ||
		(buf[8] != Erase && buf[8] != Eraseext) ||
		buf[9] != Writeprot ||
		buf[10] != Writeunprot ||
		buf[11] != Readprot ||
		buf[12] != Readunprot) {
		fprint(2, "command code mismatch, run with -d\n");
		return;
	}

	// get the device id
	if(debug)
		fprint(2, "getting device id\n");
	buf[0] = Getid; buf[1] = Full ^ Getid;
	if(write(dev, buf, 2) != 2) {
		fprint(2, "unable to write\n");
		exits("protoerr");;
	}

	if(readn(dev, buf, 1) == 0 || buf[0] != Ack) {
		fprint(2, "'get id' command rejected\n");
		exits("protoerr");;
	}
	if(readn(dev, buf, 4) == 0 || buf[0] != 1 || buf[3] != Ack) {
		fprint(2, "protocol error while getting device id\n");
		exits("protoerr");;
	}

	fprint(2, "device id: %x%x\n", buf[1], buf[2]);
}

void
stm_protect(int dev, int acmd)
{
	char *cmds;
	int cmd;

	switch(acmd) {
	case Doreadprot: cmd = Readprot; cmds = "read protect"; break;
	case Doreadunprot: cmd = Readunprot; cmds = "read unprotect"; break;
	case Dowriteunprot: cmd = Writeunprot; cmds = "write unprotect"; break;
	default:
		fprint(2, "stm_protect does not handle this command\n");
		exits("apperr");
	}

	if(debug)
		fprint(2, "%s device\n", cmds);

	buf[0] = cmd; buf[1] = Full ^ cmd;
	if(write(dev, buf, 2) != 2) {
		fprint(2, "unable to write\n");
		exits("deverr");;
	}

	if(readn(dev, buf, 1) == 0 || buf[0] != Ack) {
		fprint(2, "'%s' command rejected\n", cmds);
		exits("protoerr");;
	}

	buf[0] = Ack;
	if(write(dev, buf, 1) != 1) {
		fprint(2, "unable to ack %s", cmds);
		exits("deverr");;
	}

	if(readn(dev, buf, 1) == 0 || buf[0] != Ack) {
		fprint(2, "'%s' unable to ack\n", cmds);
		exits("protoerr");;
	}

	fprint(2, "%s done\n", cmds);
}

void
stm_readpage(int dev, uvlong addr, uchar sz) // size - 1
{
	if(addr + (sz + 1) < addr) {
		fprint(2, "addr + sz < addr; please check values\n");
		exits("protoerr");
	}

	buf[0] = Read; buf[1] = Full ^ Read;
	if(write(dev, buf, 2) != 2) {
		fprint(2, "unable to write\n");
		exits("deverr");
	}

	if(readn(dev, buf, 1) == 0 || buf[0] != Ack) {
		fprint(2, "'read memory' command rejected\n");
		exits("protoerr");
	}

	// send the start address
	buf[0] = (addr >> 24) & 0xFF;
	buf[1] = (addr >> 16) & 0xFF;
	buf[2] = (addr >> 8) & 0xFF;
	buf[3] = addr & 0xFF;
	buf[4] = buf[0] ^ buf[1] ^ buf[2] ^ buf[3];

	if(write(dev, buf, 5) != 5) {
		fprint(2, "unable to write\n");
		exits("deverr");
	}

	if(readn(dev, buf, 1) == 0 || buf[0] != Ack) {
		fprint(2, "read memory address rejected\n");
		exits("protoerr");
	}

	// send the size
	buf[0] = sz; buf[1] = Full ^ buf[0];

	if(write(dev, buf, 2) != 2) {
		fprint(2, "unable to write\n");
		exits("deverr");
	}

	if(readn(dev, buf, 1) == 0 || buf[0] != Ack) {
		fprint(2, "read command rejected; check address and count\n");
		exits("protoerr");
	}

	// read contents
	int c = readn(dev, buf, sz + 1);
	if(c != sz + 1) {
		fprint(2, "memory read error, address 0x%08llx, size-1 0x%02x, c %d\n", addr, sz, c);
		exits("apperr");
	}
	write(1, buf, c);
}

void
stm_writepage(int dev, uvlong addr, uchar sz)
{
	if(addr + sz < addr) {
		fprint(2, "addr + sz < addr; please check values\n");
		exits("protoerr");
	}

	buf[0] = Write; buf[1] = Full ^ Write;
	if(write(dev, buf, 2) != 2) {
		fprint(2, "unable to write\n");
		exits("deverr");
	}

	if(readn(dev, buf, 1) == 0 || buf[0] != Ack) {
		fprint(2, "'write memory' command rejected 0x%02x\n", buf[0]);
		exits("protoerr");
	}

	// send the start address
	buf[0] = (addr >> 24) & 0xFF;
	buf[1] = (addr >> 16) & 0xFF;
	buf[2] = (addr >> 8) & 0xFF;
	buf[3] = addr & 0xFF;
	buf[4] = buf[0] ^ buf[1] ^ buf[2] ^ buf[3];

	if(write(dev, buf, 5) != 5) {
		fprint(2, "unable to write\n");
		exits("deverr");
	}

	if(readn(dev, buf, 1) == 0 || buf[0] != Ack) {
		fprint(2, "write memory address rejected\n");
		exits("protoerr");
	}

	// send the size
	buf[0] = sz - 1;

	if(write(dev, buf, 1) != 1) {
		fprint(2, "unable to write\n");
		exits("deverr");
	}

	// write contents
	int c = readn(0, buf, sz);
	if(c != sz) {
		fprint(2, "error reading stdin\n");
		exits("apperr");
	}
	if(write(dev, buf, c) != c) {
		fprint(2, "unable to write data\n");
		exits("deverr");
	}

	// write data checksum
	buf[0] = (sz - 1) ^ buf[0];
	for(int i = 1; i < c; i++)
		buf[0] ^= buf[i];

	if(write(dev, buf, 1) != 1) {
		fprint(2, "unable to write checksum\n");
		exits("deverr");
	}

	// ack the command and finish
	if(readn(dev, buf, 1) == 0 || buf[0] != Ack) {
		fprint(2, "write data rejected, got 0x%02x\n", buf[0]);
		exits("protoerr");
	}
}

void
stm_read(int dev, uvlong addr, uvlong sz)
{
	if(sz == 0) {
		fprint(2, "read size cannot be 0\n");
		exits("protoerr");
	}
	if(addr + sz < addr) {
		fprint(2, "addr + sz < addr; please check values\n");
		exits("protoerr");
	}

	fprint(2, "reading device memory 0x%08llx, size: 0x%08llx\n", addr, sz);

	// start read memory command
	uvlong caddr = addr;
	uvlong csz = sz;

	while(caddr < (addr + sz)) {
		uchar rsz = 0xFF;
		if(csz < 0x100)
			rsz = csz - 1;

		stm_readpage(dev, caddr, rsz);

		caddr = caddr + (rsz + 1);
		csz = csz - (rsz + 1);
	}

	fprint(2, "memory read\n");
}

void
stm_write(int dev, uvlong addr, uvlong sz)
{
	if(sz == 0) {
		fprint(2, "write size cannot be 0\n");
		exits("protoerr");
	}
	if(addr & 0x3 || sz & 0x3) {
		fprint(2, "address and lenght must be 4 byte aligned\n");
		exits("apperr");
	}
	if(addr + sz < addr) {
		fprint(2, "addr + sz < addr; please check values\n");
		exits("protoerr");
	}

	fprint(2, "writing device memory 0x%08llx, size: 0x%08llx\n", addr, sz);

	// start read memory command
	uvlong caddr = addr, laddr = addr;
	uvlong csz = sz;

	while(caddr < (addr + sz)) {
		uchar rsz = 0x20;
		if(csz < 0x20)
			rsz = csz;

		stm_writepage(dev, caddr, rsz);

		if(debug && caddr - laddr >= 2048) {
			fprint(2, "progress: 0x%08llx / 0x%08llx\n", caddr, addr + sz);
			laddr = caddr;
		}
		caddr = caddr + rsz;
		csz = csz - rsz;
	}

	fprint(2, "memory written\n");
}

void
stm_erase(int dev, uint n, uchar spage)
{
	if(n > 256) {
		fprint(2, "unable to erase more than 256 pages\n");
		exits("apperr");
	}

	if(debug)
		fprint(2, "erasing memory, %d pages, start %d\n", n, spage);

	uchar csum = 0;

	// send erase command
	buf[0] = Erase; buf[1] = Full ^ Erase;
	if(write(dev, buf, 2) != 2) {
		fprint(2, "unable to write: %r\n");
		exits("deverr");
	}

	if(readn(dev, buf, 1) == 0 || buf[0] != Ack) {
		fprint(2, "'erase' rejected\n");
		exits("protoerr");
	}

	buf[0] = n - 1;
	if(write(dev, buf, 1) != 1) {
		fprint(2, "unable to write: %r\n");
		exits("deverr");
	}

	// full erase
	if(n == 256) {
		if(debug)
			fprint(2, "trying a full erase\n");

		buf[0] = 0;
		if(write(dev, buf, 1) != 1) {
			fprint(2, "unable to write: %r\n");
			exits("deverr");
		}

		if(readn(dev, buf, 1) == 0 || buf[0] != Ack) {
			fprint(2, "unable to fully erase, got 0x%02x\n", buf[0]);
			exits("protoerr");
		}

		if(debug)
			fprint(2, "performed a full erase\n");

		return;
	}

	csum = n - 1;
	for(int i = 0; i < n; i++) {
		buf[i] = spage + i;
		csum ^= buf[i];
	}
	buf[n] = csum;
	for(int i = 0; i < n + 1; i++)
		fprint(2, "0x%02x ", buf[i]);
	fprint(2, "\n");

	if(write(dev, buf, (uint)n + 1) != (uint)n + 1) {
		fprint(2, "unable to write: %r\n");
		exits("deverr");
	}

	if(readn(dev, buf, 1) == 0 || buf[0] != Ack) {
		fprint(2, "'erase' rejected\n");
		exits("protoerr");
	}

	if(debug)
		fprint(2, "erased successfully performed\n");
}

void
stm_go(int dev, uvlong addr)
{
	if(debug)
		fprint(2, "device requested to jump to 0x%08llx\n", addr);

	// get the protocol version and commands
	buf[0] = Go; buf[1] = Full ^ Go;
	if(write(dev, buf, 2) != 2) {
		fprint(2, "unable to write: %r\n");
		exits("deverr");
	}

	if(readn(dev, buf, 1) == 0 || buf[0] != Ack) {
		fprint(2, "'go' rejected\n");
		exits("protoerr");
	}

	// send the start address
	buf[0] = (addr >> 24) & 0xFF;
	buf[1] = (addr >> 16) & 0xFF;
	buf[2] = (addr >> 8) & 0xFF;
	buf[3] = addr & 0xFF;
	buf[4] = buf[0] ^ buf[1] ^ buf[2] ^ buf[3];

	if(write(dev, buf, 5) != 5) {
		fprint(2, "unable to write\n");
		exits("protoerr");
	}

	if(readn(dev, buf, 1) == 0 || buf[0] != Ack) {
		fprint(2, "go address rejected\n");
		exits("protoerr");
	}

	fprint(2, "device jumped to 0x%08llx\n", addr);
}

void
stm_checksum(int dev)
{
}

void
usage(void)
{
	fprint(2, "usage: %s [-d] [-D device] [-b baud] command args ..\n", argv0);
	fprint(2, "	info\n");
	fprint(2, "	read 	addr size\n");
	fprint(2, "	write 	addr size\n");
	fprint(2, "	erase	page n\n");
	fprint(2, "	flash	addr size\n");
	fprint(2, "	go		addr\n");
	fprint(2, "	readp, readunp, writeunp\n");
	fprint(2, "	TODO: writep,csum\n");
	exits("usage");
}

void
main(int argc, char **argv)
{
	char* devpath = "/dev/eia0";
	char* baudstr = "9600";
	char tmp[256];
	int action = -1;
	int dev = -1;

	uvlong addr = 0;
	uvlong sz = 0;

	ARGBEGIN {
	case 'b':
		baudstr = ARGF();
		break;
	case 'd':
		debug = 1;
		break;
	case 'D':
		devpath = EARGF(usage());
		break;
	default: usage();
	} ARGEND

	if(argv[0] == 0)
		usage();

	if(!strcmp(argv[0], "info"))
		action = Doinfo;
	if(!strcmp(argv[0], "read")) {
		if(argv[1] == 0 || argv[2] == 0)
			usage();
		addr = strtoull(argv[1], nil, 16);
		sz = strtoull(argv[2], nil, 16);
		action = Doread;
	}
	if(!strcmp(argv[0], "write")) {
		if(argv[1] == 0 || argv[2] == 0)
			usage();
		addr = strtoull(argv[1], nil, 16);
		sz = strtoull(argv[2], nil, 16);
		action = Dowrite;
	}
	if(!strcmp(argv[0], "erase")) {
		if(argv[1] == 0 || argv[2] == 0)
			usage();
		addr = strtoull(argv[1], nil, 16);
		sz = strtoull(argv[2], nil, 16);
		action = Doerase;
	}
	if(!strcmp(argv[0], "flash")) {
		if(argv[1] == 0 || argv[2] == 0)
			usage();
		addr = strtoull(argv[1], nil, 16);
		sz = strtoull(argv[2], nil, 16);
		action = Doflash;
	}
	if(!strcmp(argv[0], "go")) {
		if(argv[1] == 0)
			usage();
		addr = strtoull(argv[1], nil, 16);
		action = Dogo;
	}
	if(!strcmp(argv[0], "readp"))
		action = Doreadprot;
	if(!strcmp(argv[0], "readunp"))
		action = Doreadunprot;
	if(!strcmp(argv[0], "writeunp"))
		action = Doreadunprot;
	if(!strcmp(argv[0], "csum"))
		action = Docsum;
	if(action < 0)
		usage();

	// set serial operating parameters
	if(debug)
		fprint(2, "setting modem to b%s 8E1\n", baudstr);
	int devctl = open(smprint("%sctl", devpath), OWRITE);
	if(devctl < 0) {
		fprint(2, "unable to open device ctl %sctl: %r\n", devpath);
		exits("deverr");
	}

	sprint(tmp, "b%s\n", baudstr);
	if(write(devctl, tmp, 8) < 0) {
		fprint(2, "error: unable to set serial baud: %r\n");
		exits("deverr");
	}
	if(write(devctl, "l8\n", 3) < 0) {
		fprint(2, "error: unable to set serial length: %r\n");
		exits("deverr");
	}
	if(write(devctl, "pe\n", 3) < 0) {
		fprint(2, "error: unable to set serial parity: %r\n");
		exits("deverr");
	}
	if(write(devctl, "s1\n", 3) < 0) {
		fprint(2, "error: unable to set serial stop bits: %r\n");
		exits("deverr");
	}
	// if(write(devctl, "f\n", 3) < 0) {
	// 	fprint(2, "error: unable to flush the serial line: %r\n");
	// 	exits("deverr");
	// }

	if(debug)
		fprint(2, "modem set up accordingly\n");
	close(devctl);

	// opening serial line
	if(debug)
		fprint(2, "opening %s\n", devpath);
	dev = open(devpath, ORDWR);
	if(dev < 0) {
		fprint(2, "unable to open device %s: %r\n", devpath);
		exits("deverr");
	}

	// initiate connection; send Conninit, and wait for Ack
	if(debug)
		fprint(2, "initiating connection\n");
	buf[0] = Conninit;
	if(write(dev, buf, 1) != 1) {
		fprint(2, "error writing while initiating\n");
		exits("connerr");
	}

	if(readn(dev, buf, 1) != 1 || buf[0] != Ack) {
		fprint(2, "connection could not be initiated, got %x\n", buf[0]);
		exits("connerr");
	}

	// handle action
	switch(action) {
	case Doinfo: stm_info(dev); break;
	case Doread: stm_read(dev, addr, sz); break;
	case Dowrite: stm_write(dev, addr, sz); break;
	case Doerase: stm_erase(dev, addr, sz); break;
	case Doflash:
		stm_erase(dev, 256, 0);
		stm_write(dev, addr, sz);
		stm_go(dev, addr);
		break;
	case Doreadprot:
	case Doreadunprot:
	case Dowriteunprot:
		stm_protect(dev, action); break;
	case Dogo: stm_go(dev, addr); break;
	case Docsum: stm_checksum(dev); break;
	default: exits("apperr");
	}

	if(debug)
		fprint(2, "exiting\n");

	exits(nil);
}