[coreboot] [flashrom] [PATCH] external serial flasher protocol support

Urja Rannikko urjaman at gmail.com
Sun Jun 28 17:49:47 CEST 2009


Support for an external serial flasher protocol.
Supports RS-232, USB serial converters (untested) and TCP streams.

Signed-off-by:  Urja Rannikko <urjaman at gmail.com>
---

Hi all,

New patch, and AVR code attached; the AVR code was changed to
implement writeb so that it takes 5 bytes of opbuf instead of 6, the
AVR code doesnt support query read_n maximum or set bustype, because
these commands are not necessary for this implementation/device.

The flashrom driver was changed to implement querying for read_n
maximum, and some consistency fixes. Setting bustype will need to wait
until programmer drivers can see current flashchip definition, i
already posted a simple patch for this (using a global pointer), but
carldani seemed to like the way of passing the pointer through the
code to the flashrom driver, either way is ok for me, but i dont want
to do the parameter passing patch :P.
Also the chips with bustype of "Non-SPI" will need to get fixed (to
FWH (and or) LPC or Parallel), because it will be impossible for the
programmer to make a proper choice if it is said to use FWH,LPC or
parallel. I will help with this task next btw.

I didnt get to test any of this on real hardware, because i dont
currently have access to it, but it should work.
I'll also attach the protocol spec here, so all stuff that belongs
together is in this single mail.

Also:
> flash chips in the future (oh, somebody will, i know). My current view
> is that when this is needed, the protocol can extend by having the 6
> commands currently using 24-bit values get an _EXT version for 32-bit
> values, and document how the 24-bit addresses map with 32-bit
> addresses. (i'd say that the 24-bit address space is top mapped in the
> 32-bit space, but not sure)

Or we could use the protocol version field to make protocol v2 with
32-bit fields, if we really dont want to duplicate commands.

And patch inlined:
Index: serprog.c
===================================================================
--- serprog.c	(revision 633)
+++ serprog.c	(working copy)
@@ -39,9 +39,747 @@

 char *serprog_param = NULL;

-#define SERPROG_SUPPORT 0
+#define SERPROG_SUPPORT 1
+
 #if SERPROG_SUPPORT == 1
+
+#define MSGHEADER "serprog:"
+
+#define S_ACK 0x06
+#define S_NAK 0x15
+#define S_CMD_NOP		0x00	/* No operation                                 */
+#define S_CMD_Q_IFACE		0x01	/* Query interface version                      */
+#define S_CMD_Q_CMDMAP		0x02	/* Query supported commands bitmap              */
+#define S_CMD_Q_PGMNAME		0x03	/* Query programmer name
        */
+#define S_CMD_Q_SERBUF		0x04	/* Query Serial Buffer Size                     */
+#define S_CMD_Q_BUSTYPE		0x05	/* Query supported bustypes
        */
+#define S_CMD_Q_CHIPSIZE	0x06	/* Query supported chipsize (2^n
format)        */
+#define S_CMD_Q_OPBUF		0x07	/* Query operation buffer size                  */
+#define S_CMD_Q_WRNMAXLEN	0x08	/* Query opbuf-write-N maximum lenght
         */
+#define S_CMD_R_BYTE		0x09	/* Read a single byte                           */
+#define S_CMD_R_NBYTES		0x0A	/* Read n bytes                                 */
+#define S_CMD_O_INIT		0x0B	/* Initialize operation buffer                  */
+#define S_CMD_O_WRITEB		0x0C	/* Write opbuf: Write byte with address         */
+#define S_CMD_O_WRITEN		0x0D	/* Write to opbuf: Write-N                      */
+#define S_CMD_O_DELAY		0x0E	/* Write opbuf: udelay                          */
+#define S_CMD_O_EXEC		0x0F	/* Execute operation buffer                     */
+#define S_CMD_SYNCNOP		0x10	/* Special no-operation that returns NAK+ACK    */
+#define S_CMD_Q_RDNMAXLEN	0x11	/* Query read-n maximum length			*/
+#define S_CMD_S_BUSTYPE		0x12	/* Set used bustype(s).				*/
+
+static int sp_fd;
+
+static uint16_t sp_device_serbuf_size = 16;
+static uint16_t sp_device_opbuf_size = 300;
+/* Bitmap of supported commands */
+static uint8_t sp_cmdmap[32];
+
+/* sp_prev_was_write used to detect writes with continouous addresses
+	and combine them to write-n's */
+static int sp_prev_was_write = 0;
+/* sp_write_n_addr used as the starting addr of the currently
+	combined write-n operation */
+static uint32_t sp_write_n_addr;
+/* The maximum length of an write_n operation; 0 = write-n not supported */
+static uint32_t sp_max_write_n = 0;
+/* The maximum length of a read_n operation; 0 = 2^24 */
+static uint32_t sp_max_read_n = 0;
+
+/* A malloc'd buffer for combining the operation's data
+	and a counter that tells how much data is there. */
+static uint8_t *sp_write_n_buf;
+static uint32_t sp_write_n_bytes = 0;
+
+/* sp_streamed_* used for flow control checking */
+static int sp_streamed_transmit_ops = 0;
+static int sp_streamed_transmit_bytes = 0;
+
+/* sp_opbuf_usage used for counting the amount of
+	on-device operation buffer used */
+static int sp_opbuf_usage = 0;
+/* if true causes sp_docommand to automatically check
+	whether the command is supported before doing it */
+static int sp_check_avail_automatic = 0;
+
+static void sp_die(char *msg)
+{
+	perror(msg);
+	exit(1);
+}
+
+static int sp_opensocket(char *ip, unsigned int port)
+{
+	int flag = 1;
+	struct hostent *hostPtr = NULL;
+	struct sockaddr_in sp;
+	int sock;
+	printf_debug(MSGHEADER "IP %s port %d\n", ip, port);
+	sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
+	if (sock < 0)
+		sp_die("Error: serprog cannot open socket");
+	hostPtr = gethostbyname(ip);
+	if (NULL == hostPtr) {
+		hostPtr = gethostbyaddr(ip, strlen(ip), AF_INET);
+		if (NULL == hostPtr)
+			sp_die("Error: cannot resolve");
+	}
+	memset(&sp, 0, sizeof(sp));
+	sp.sin_family = AF_INET;
+	sp.sin_port = htons(port);
+	(void)memcpy(&sp.sin_addr, hostPtr->h_addr, hostPtr->h_length);
+	if (connect(sock, (struct sockaddr *)&sp, sizeof(sp)) < 0) {
+		close(sock);
+		sp_die("Error: serprog cannot connect");
+	}
+	/* We are latency limited, and sometimes do write-write-read    *
+	 * (write-n) - so enable TCP_NODELAY.				*/
+	setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &flag, sizeof(int));
+	return sock;
+}
+
+struct baudentry {
+	int flag;
+	unsigned int baud;
+};
+
+/* I'd like if the C preprocessor could have directives in macros */
+#define BAUDENTRY(baud) { B##baud, baud },
+static const struct baudentry sp_baudtable[] = {
+	BAUDENTRY(9600)
+	BAUDENTRY(19200)
+	BAUDENTRY(38400)
+	BAUDENTRY(57600)
+	BAUDENTRY(115200)
+#ifdef B230400
+	BAUDENTRY(230400)
+#endif
+#ifdef B460800
+	BAUDENTRY(460800)
+#endif
+#ifdef B500000
+	BAUDENTRY(500000)
+#endif
+#ifdef B576000
+	BAUDENTRY(576000)
+#endif
+#ifdef B921600
+	BAUDENTRY(921600)
+#endif
+#ifdef B1000000
+	BAUDENTRY(1000000)
+#endif
+#ifdef B1152000
+	BAUDENTRY(1152000)
+#endif
+#ifdef B1500000
+	BAUDENTRY(1500000)
+#endif
+#ifdef B2000000
+	BAUDENTRY(2000000)
+#endif
+#ifdef B2500000
+	BAUDENTRY(2500000)
+#endif
+#ifdef B3000000
+	BAUDENTRY(3000000)
+#endif
+#ifdef B3500000
+	BAUDENTRY(3500000)
+#endif
+#ifdef B4000000
+	BAUDENTRY(4000000)
+#endif
+	{0, 0}			/* Terminator */
+};
+
+static int sp_openserport(char *dev, unsigned int baud)
+{
+	struct termios options;
+	int fd, i;
+	fd = open(dev, O_RDWR | O_NOCTTY | O_NDELAY);
+	if (fd < 0)
+		sp_die("Error: cannot open serial port");
+	fcntl(fd, F_SETFL, 0);
+	tcgetattr(fd, &options);
+	for (i = 0;; i++) {
+		if (sp_baudtable[i].baud == 0) {
+			close(fd);
+			fprintf(stderr,
+				"Error: cannot configure for baudrate %d\n",
+				baud);
+			exit(1);
+		}
+		if (sp_baudtable[i].baud == baud) {
+			cfsetispeed(&options, sp_baudtable[i].flag);
+			cfsetospeed(&options, sp_baudtable[i].flag);
+			break;
+		}
+	}
+	options.c_cflag &= ~PARENB;
+	options.c_cflag &= ~CSTOPB;
+	options.c_cflag &= ~CSIZE;
+	options.c_cflag |= CS8;
+	options.c_cflag &= ~CRTSCTS;
+	options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
+	options.c_iflag &= ~(IXON | IXOFF | IXANY);
+	options.c_oflag &= ~OPOST;
+	options.c_cflag |= (CLOCAL | CREAD);
+	tcsetattr(fd, TCSANOW, &options);
+	return fd;
+}
+
+static void sp_flush_incoming(void)
+{
+	int i;
+	for (i=0;i<100;i++) { /* In case the device doesnt do EAGAIN, just read 0 */
+		unsigned char flush[16];
+		ssize_t rv;
+		rv = read(sp_fd, flush, sizeof(flush));
+		if ((rv == -1) && (errno == EAGAIN))
+			break;
+		if (rv == -1)
+			sp_die("flush read");
+	}
+	return;
+}
+
+static int sp_sync_read_timeout(int loops)
+{
+	int i;
+	unsigned char c;
+	for (i = 0; i < loops; i++) {
+		ssize_t rv;
+		rv = read(sp_fd, &c, 1);
+		if (rv == 1)
+			return c;
+		if ((rv == -1) && (errno != EAGAIN))
+			sp_die("read");
+		usleep(10 * 1000);	/* 10ms units */
+	}
+	return -1;
+}
+
+/* Synchronize: a bit tricky algorhytm that tries to (and in my tests has *
+ * always succeeded in) bring the serial protocol to known waiting-for-   *
+ * command state - uses nonblocking read - rest of the driver uses	  *
+ * blocking read - TODO: add an alarm() timer for the rest of the app on  *
+ * serial operations, though not such a big issue as the first thing to   *
+ * do is synchronize (eg. check that device is alive).			  */
+static void sp_synchronize(void)
+{
+	int i;
+	int flags = fcntl(sp_fd, F_GETFL);
+	unsigned char buf[8];
+	flags |= O_NONBLOCK;
+	fcntl(sp_fd, F_SETFL, flags);
+	/* First sends 8 NOPs, then flushes the return data - should cause *
+	 * the device serial parser to get to a sane state, unless if it   *
+	 * is waiting for a real long write-n.                             */
+	memset(buf, S_CMD_NOP, 8);
+	if (write(sp_fd, buf, 8) != 8)
+		sp_die("flush write");
+	/* A second should be enough to get all the answers to the buffer */
+	usleep(1000 * 1000);
+	sp_flush_incoming();
+
+	/* Then try upto 8 times to send syncnop and get the correct special *
+	 * return of NAK+ACK. Timing note: upto 10 characters, 10*50ms =     *
+	 * upto 500ms per try, 8*0.5s = 4s; +1s (above) = upto 5s sync       *
+	 * attempt, ~1s if immediate success.                                */
+	for (i = 0; i < 8; i++) {
+		int n;
+		unsigned char c = S_CMD_SYNCNOP;
+		if (write(sp_fd, &c, 1) != 1)
+			sp_die("sync write");
+		printf_debug(".");
+		fflush(stdout);
+		for (n = 0; n < 10; n++) {
+			c = sp_sync_read_timeout(5);	/* wait upto 50ms */
+			if (c != S_NAK)
+				continue;
+			c = sp_sync_read_timeout(2);
+			if (c != S_ACK)
+				continue;
+			c = S_CMD_SYNCNOP;
+			if (write(sp_fd, &c, 1) != 1)
+				sp_die("sync write");
+			c = sp_sync_read_timeout(50);
+			if (c != S_NAK)
+				break;	/* fail */
+			c = sp_sync_read_timeout(10);
+			if (c != S_ACK)
+				break;	/* fail */
+			/* Ok, synchronized; back to blocking reads and return. */
+			flags &= ~O_NONBLOCK;
+			fcntl(sp_fd, F_SETFL, flags);
+			printf_debug("\n");
+			return;
+		}
+	}
+	fprintf(stderr,
+		"Error: cannot synchronize protocol\n"
+		"- check communications and reset device?\n");
+	exit(1);
+}
+
+static int sp_check_commandavail(uint8_t command)
+{
+	int byteoffs, bitoffs;
+	byteoffs = command / 8;
+	bitoffs = command % 8;
+	return (sp_cmdmap[byteoffs] & (1 << bitoffs)) ? 1 : 0;
+}
+
+static int sp_automatic_cmdcheck(uint8_t cmd)
+{
+	if ((sp_check_avail_automatic) && (sp_check_commandavail(cmd) == 0)) {
+		printf_debug ("Warning: Automatic command availability check"
+				" failed for cmd %d - wont execute cmd\n",cmd);
+		return 1;
+		}
+	return 0;
+}
+
+static int sp_docommand(uint8_t command, uint32_t parmlen,
+			     uint8_t * params, uint32_t retlen, void *retparms)
+{
+	unsigned char *sendpacket;
+	unsigned char c;
+	if (sp_automatic_cmdcheck(command))
+		return 1;
+	sendpacket = malloc(1 + parmlen);
+	if (!sendpacket)
+		sp_die("Error: cannot malloc command buffer");
+	sendpacket[0] = command;
+	memcpy(&(sendpacket[1]), params, parmlen);
+	if (write(sp_fd, sendpacket, 1 + parmlen) != (1 + parmlen)) {
+		sp_die("Error: cannot write command");
+	}
+	free(sendpacket);
+	if (read(sp_fd, &c, 1) != 1)
+		sp_die("Error: cannot read from device");
+	if (c == S_NAK) return 1;
+	if (c != S_ACK) {
+		fprintf(stderr,
+			"Error: invalid response 0x%02X from device\n",c);
+		exit(1);
+	}
+	if (retlen) {
+		int rd_bytes = 0;
+		do {
+			int r;
+			r = read(sp_fd, retparms + rd_bytes,
+				 retlen - rd_bytes);
+			if (r <= 0) sp_die
+				    ("Error: cannot read return parameters");
+			rd_bytes += r;
+		} while (rd_bytes != retlen);
+	}
+	return 0;
+}
+
+static void sp_flush_stream(void)
+{
+	if (sp_streamed_transmit_ops)
+		do {
+			unsigned char c;
+			if (read(sp_fd, &c, 1) != 1) {
+				sp_die
+				    ("Error: cannot read from device (flushing stream)");
+			}
+			if (c == S_NAK) {
+				fprintf(stderr,
+					"Error: NAK to a stream buffer operation\n");
+				exit(1);
+			}
+			if (c != S_ACK) {
+				fprintf(stderr,
+					"Error: Invalid reply 0x%02X from device\n",
+					c);
+				exit(1);
+			}
+		} while (--sp_streamed_transmit_ops);
+	sp_streamed_transmit_ops = 0;
+	sp_streamed_transmit_bytes = 0;
+}
+
+static int sp_stream_buffer_op(uint8_t cmd, uint32_t parmlen, uint8_t * parms)
+{
+	uint8_t *sp;
+	if (sp_automatic_cmdcheck(cmd))
+		return 1;
+	sp = malloc(1 + parmlen);
+	if (!sp) sp_die("Error: cannot malloc command buffer");
+	sp[0] = cmd;
+	memcpy(&(sp[1]), parms, parmlen);
+	if (sp_streamed_transmit_bytes >= (1 + parmlen + sp_device_serbuf_size))
+		sp_flush_stream();
+	if (write(sp_fd, sp, 1 + parmlen) != (1 + parmlen))
+		sp_die("Error: cannot write command");
+	free(sp);
+	sp_streamed_transmit_ops += 1;
+	sp_streamed_transmit_bytes += 1 + parmlen;
+	return 0;
+}
+
+int serprog_init(void)
+{
+	uint16_t iface;
+	int len;
+	unsigned char pgmname[17];
+	unsigned char rbuf[3];
+	unsigned char c;
+	char *num;
+	char *dev;
+	printf_debug("%s\n", __func__);
+	/* the parameter is either of format "/dev/device:baud" or "ip:port" */
+	if ((!serprog_param) || (!strlen(serprog_param))) {
+		nodevice:
+		fprintf(stderr,
+			"Error: No device/host given for the serial programmer driver.\n"
+			"Use flashrom -p serprog=/dev/device:baud or flashrom -p
serprog=ip:port\n");
+		exit(1);
+	}
+	num = strstr(serprog_param, ":");
+	len = num - serprog_param;
+	if (!len) goto nodevice;
+	if (!num) {
+		fprintf(stderr,
+			"Error: No port or baudrate specified to serial programmer driver.\n"
+			"Use flashrom -p serprog=/dev/device:baud or flashrom -p
serprog=ip:port\n");
+		exit(1);
+	}
+	len = num - serprog_param;
+	dev = malloc(len + 1);
+	if (!dev) sp_die("Error: memory allocation failure");
+	memcpy(dev, serprog_param, len);
+	dev[len] = 0;
+	num = strdup(num + 1);
+	if (!num) sp_die("Error: memory allocation failure");
+	free(serprog_param);
+	serprog_param = NULL;
+
+	if (dev[0] == '/') sp_fd = sp_openserport(dev, atoi(num));
+	else sp_fd = sp_opensocket(dev, atoi(num));
+
+	free(dev); dev = NULL;
+	free(num); num = NULL;
+
+	printf_debug(MSGHEADER "connected - attempting to synchronize\n");
+
+	sp_check_avail_automatic = 0;
+
+	sp_synchronize();
+
+	printf_debug(MSGHEADER "Synchronized\n");
+
+	if (sp_docommand(S_CMD_Q_IFACE, 0, NULL, 2, &iface)) {
+		fprintf(stderr, "Error: NAK to Query Interface version\n");
+		exit(1);
+	}
+
+	if (iface != 1) {
+		fprintf(stderr, "Error: Unknown interface version %d\n", iface);
+		exit(1);
+	}
+
+	printf_debug(MSGHEADER "Interface version ok.\n");
+
+	if (sp_docommand(S_CMD_Q_CMDMAP, 0, NULL, 32, sp_cmdmap)) {
+		fprintf(stderr, "Error: query command map not supported\n");
+		exit(1);
+	}
+
+	sp_check_avail_automatic = 1;
+
+	/* Check for the minimum operational set of commands */
+	if (sp_check_commandavail(S_CMD_R_BYTE) == 0) {
+		fprintf(stderr, "Error: Single byte read not supported\n");
+		exit(1);
+	}
+	/* This could be translated to single byte reads (if missing),	*
+	 * but now we dont support that.				*/
+	if (sp_check_commandavail(S_CMD_R_NBYTES) == 0) {
+		fprintf(stderr, "Error: Read n bytes not supported\n");
+		exit(1);
+	}
+	/* In the future one could switch to read-only mode if these	*
+	 * are not available.						*/
+	if (sp_check_commandavail(S_CMD_O_INIT) == 0) {
+		fprintf(stderr,
+			"Error: Initialize operation buffer not supported\n");
+		exit(1);
+	}
+	if (sp_check_commandavail(S_CMD_O_WRITEB) == 0) {
+		fprintf(stderr,
+			"Error: Write to opbuf: write byte not supported\n");
+		exit(1);
+	}
+	if (sp_check_commandavail(S_CMD_O_DELAY) == 0) {
+		fprintf(stderr, "Error: Write to opbuf: delay not supported\n");
+		exit(1);
+	}
+	if (sp_check_commandavail(S_CMD_O_EXEC) == 0) {
+		fprintf(stderr,
+			"Error: Execute operation buffer not supported\n");
+		exit(1);
+	}
+
+	if (sp_docommand(S_CMD_Q_PGMNAME, 0, NULL, 16, pgmname)) {
+		fprintf(stderr, "Warning: NAK to query programmer name\n");
+		strcpy((char *)pgmname, "(unknown)");
+	}
+	pgmname[16] = 0;
+	printf(MSGHEADER "Programmer name \"%s\"\n", pgmname);
+
+	if (sp_docommand(S_CMD_Q_SERBUF, 0, NULL, 2, &sp_device_serbuf_size)) {
+		fprintf(stderr, "Warning: NAK to query serial buffer size\n");
+	}
+	printf_debug(MSGHEADER "serial buffer size %d\n",
+		     sp_device_serbuf_size);
+
+	if (sp_docommand(S_CMD_Q_OPBUF, 0, NULL, 2, &sp_device_opbuf_size)) {
+		fprintf(stderr,
+			"Warning: NAK to query operation buffer size\n");
+	}
+	printf_debug(MSGHEADER "operation buffer size %d\n",
+		     sp_device_opbuf_size);
+
+	if (sp_docommand(S_CMD_Q_BUSTYPE, 0, NULL, 1, &c)) {
+		fprintf(stderr, "Warning: NAK to query supported buses\n");
+		c = CHIP_BUSTYPE_NONSPI;	/* A reasonable default for now. */
+	}
+	buses_supported = c;
+
+	if (sp_docommand(S_CMD_O_INIT, 0, NULL, 0, NULL)) {
+		fprintf(stderr, "Error: NAK to initialize operation buffer\n");
+		exit(1);
+	}
+
+	if (sp_docommand(S_CMD_Q_WRNMAXLEN, 0, NULL, 3, rbuf)) {
+		printf_debug(MSGHEADER "Write-n not supported");
+		sp_max_write_n = 0;
+	} else {
+		sp_max_write_n = ((unsigned int)(rbuf[0]) << 0);
+		sp_max_write_n |= ((unsigned int)(rbuf[1]) << 8);
+		sp_max_write_n |= ((unsigned int)(rbuf[2]) << 16);
+		printf_debug(MSGHEADER "Maximum write-n length %d\n",
+			     sp_max_write_n);
+		sp_write_n_buf = malloc(sp_max_write_n);
+		if (!sp_write_n_buf) {
+			fprintf(stderr,
+				"Error: cannot allocate memory for Write-n buffer\n");
+			exit(1);
+		}
+		sp_write_n_bytes = 0;
+	}
+	
+	if ((sp_check_commandavail(S_CMD_Q_RDNMAXLEN))
+		&&((sp_docommand(S_CMD_Q_RDNMAXLEN,0,NULL, 3, rbuf) == 0))) {
+		sp_max_read_n = ((unsigned int)(rbuf[0]) << 0);
+		sp_max_read_n |= ((unsigned int)(rbuf[1]) << 8);
+		sp_max_read_n |= ((unsigned int)(rbuf[2]) << 16);
+		printf_debug(MSGHEADER "Maximum read-n length %d\n",
+			sp_max_read_n ? sp_max_read_n : (1<<24));
+	} else {
+		printf_debug(MSGHEADER "Maximum read-n length not reported\n");
+		sp_max_read_n = 0;
+	}
+
+	sp_prev_was_write = 0;
+	sp_streamed_transmit_ops = 0;
+	sp_streamed_transmit_bytes = 0;
+	sp_opbuf_usage = 0;
+	return 0;
+}
+
+/* Move an in flashrom buffer existing write-n operation to	*
+ * the on-device operation buffer.				*/
+static void sp_pass_writen(void)
+{
+	unsigned char header[7];
+	printf_debug(MSGHEADER "Passing write-n bytes=%d addr=0x%x\n",
+		     sp_write_n_bytes, sp_write_n_addr);
+	if (sp_streamed_transmit_bytes >=
+	    (7 + sp_write_n_bytes + sp_device_serbuf_size))
+		sp_flush_stream();
+	/* In case it's just a single byte send it as a single write. */
+	if (sp_write_n_bytes == 1) {
+		sp_write_n_bytes = 0;
+		header[0] = (sp_write_n_addr >> 0) & 0xFF;
+		header[1] = (sp_write_n_addr >> 8) & 0xFF;
+		header[2] = (sp_write_n_addr >> 16) & 0xFF;
+		header[3] = sp_write_n_buf[0];
+		sp_stream_buffer_op(S_CMD_O_WRITEB, 4, header);
+		sp_opbuf_usage += 5;
+		return;
+	}
+	header[0] = S_CMD_O_WRITEN;
+	header[1] = (sp_write_n_bytes >> 0) & 0xFF;
+	header[2] = (sp_write_n_bytes >> 8) & 0xFF;
+	header[3] = (sp_write_n_bytes >> 16) & 0xFF;
+	header[4] = (sp_write_n_addr >> 0) & 0xFF;
+	header[5] = (sp_write_n_addr >> 8) & 0xFF;
+	header[6] = (sp_write_n_addr >> 16) & 0xFF;
+	if (write(sp_fd, header, 7) != 7)
+		sp_die("Error: cannot write write-n command\n");
+	if (write(sp_fd, sp_write_n_buf, sp_write_n_bytes) !=
+	    sp_write_n_bytes)
+		sp_die("Error: cannot write write-n data");
+	sp_streamed_transmit_bytes += 7 + sp_write_n_bytes;
+	sp_streamed_transmit_ops += 1;
+	sp_opbuf_usage += 7 + sp_write_n_bytes;
+	sp_write_n_bytes = 0;
+	sp_prev_was_write = 0;
+}
+
+static void sp_execute_opbuf_noflush(void)
+{
+	if ((sp_max_write_n) && (sp_write_n_bytes))
+		sp_pass_writen();
+	sp_stream_buffer_op(S_CMD_O_EXEC, 0, 0);
+	printf_debug(MSGHEADER "Executed operation buffer of %d bytes\n",
+		     sp_opbuf_usage);
+	sp_opbuf_usage = 0;
+	sp_prev_was_write = 0;
+	return;
+}
+
+static void sp_execute_opbuf(void)
+{
+	sp_execute_opbuf_noflush();
+	sp_flush_stream();
+}
+
+int serprog_shutdown(void)
+{
+	printf_debug("%s\n", __func__);
+	if ((sp_opbuf_usage) || (sp_max_write_n && sp_write_n_bytes))
+		sp_execute_opbuf();
+	close(sp_fd);
+	if (sp_max_write_n)
+		free(sp_write_n_buf);
+	return 0;
+}
+
+static void sp_check_opbuf_usage(int bytes_to_be_added)
+{
+	if (sp_device_opbuf_size <= (sp_opbuf_usage + bytes_to_be_added)) {
+		sp_execute_opbuf();
+		/* If this happens in the mid of an page load the page load *
+		 * will propably fail.					    */
+		printf_debug(MSGHEADER
+		"Warning: executed operation buffer due to size reasons\n");
+	}
+}
+
+void serprog_chip_writeb(uint8_t val, chipaddr addr)
+{
+	printf_debug("%s\n", __func__);
+	if (sp_max_write_n) {
+		if ((sp_prev_was_write)
+		    && (addr == (sp_write_n_addr + sp_write_n_bytes))) {
+			sp_write_n_buf[sp_write_n_bytes++] = val;
+		} else {
+			if ((sp_prev_was_write) && (sp_write_n_bytes))
+				sp_pass_writen();
+			sp_prev_was_write = 1;
+			sp_write_n_addr = addr;
+			sp_write_n_bytes = 1;
+			sp_write_n_buf[0] = val;
+		}
+		sp_check_opbuf_usage(7 + sp_write_n_bytes);
+		if (sp_write_n_bytes >= sp_max_write_n)
+			sp_pass_writen();
+	} else {
+		/* We will have to do single writeb ops. */
+		unsigned char writeb_parm[4];
+		sp_check_opbuf_usage(6);
+		writeb_parm[0] = (addr >> 0) & 0xFF;
+		writeb_parm[1] = (addr >> 8) & 0xFF;
+		writeb_parm[2] = (addr >> 16) & 0xFF;
+		writeb_parm[3] = val;
+		sp_stream_buffer_op(S_CMD_O_WRITEB, 4, writeb_parm);
+		sp_opbuf_usage += 5;
+	}
+}
+
+uint8_t serprog_chip_readb(const chipaddr addr)
+{
+	unsigned char c;
+	unsigned char buf[3];
+	/* Will stream the read operation - eg. add it to the stream buffer, *
+	 * then flush the buffer, then read the read answer.		     */
+	if ((sp_opbuf_usage) || (sp_max_write_n && sp_write_n_bytes))
+		sp_execute_opbuf_noflush();
+	buf[0] = ((addr >> 0) & 0xFF);
+	buf[1] = ((addr >> 8) & 0xFF);
+	buf[2] = ((addr >> 16) & 0xFF);
+	sp_stream_buffer_op(S_CMD_R_BYTE, 3, buf);
+	sp_flush_stream();
+	if (read(sp_fd, &c, 1) != 1)
+		sp_die("readb byteread");
+	printf_debug("%s addr=0x%lx returning 0x%02X\n", __func__, addr, c);
+	return c;
+}
+
+/* Local version that really does the job, doesnt care of max_read_n. */
+static void sp_do_read_n(uint8_t * buf, const chipaddr addr, size_t len)
+{
+	int rd_bytes = 0;
+	unsigned char sbuf[6];
+	printf_debug("%s: addr=0x%lx len=%d\n", __func__, addr, len);
+	/* Stream the read-n -- as above. */
+	if ((sp_opbuf_usage) || (sp_max_write_n && sp_write_n_bytes))
+		sp_execute_opbuf_noflush();
+	sbuf[0] = ((addr >> 0) & 0xFF);
+	sbuf[1] = ((addr >> 8) & 0xFF);
+	sbuf[2] = ((addr >> 16) & 0xFF);
+	sbuf[3] = ((len >> 0) & 0xFF);
+	sbuf[4] = ((len >> 8) & 0xFF);
+	sbuf[5] = ((len >> 16) & 0xFF);
+	sp_stream_buffer_op(S_CMD_R_NBYTES, 6, sbuf);
+	sp_flush_stream();
+	do {
+		int r = read(sp_fd, buf + rd_bytes, len - rd_bytes);
+		if (r <= 0)
+			sp_die("Error: cannot read read-n data");
+		rd_bytes += r;
+	} while (rd_bytes != len);
+	return;
+}
+
+/* The externally called version that makes sure that max_read_n is obeyed. */
+void serprog_chip_readn(uint8_t * buf, const chipaddr addr, size_t len)
+{
+	size_t lenm = len;
+	chipaddr addrm = addr;
+	while ((sp_max_read_n)&&(lenm > sp_max_read_n)) {
+		sp_do_read_n(&(buf[addrm-addr]),addrm,sp_max_read_n);
+		addrm += sp_max_read_n;
+		lenm -= sp_max_read_n;
+	}
+	if (lenm) sp_do_read_n(&(buf[addrm-addr]),addrm,lenm);
+}
+
+void serprog_delay(int delay)
+{
+	unsigned char buf[4];
+	printf_debug("%s\n", __func__);
+	if ((sp_max_write_n) && (sp_write_n_bytes))
+		sp_pass_writen();
+	sp_check_opbuf_usage(5);
+	buf[0] = ((delay >> 0) & 0xFF);
+	buf[1] = ((delay >> 8) & 0xFF);
+	buf[2] = ((delay >> 16) & 0xFF);
+	buf[3] = ((delay >> 24) & 0xFF);
+	sp_stream_buffer_op(S_CMD_O_DELAY, 4, buf);
+	sp_opbuf_usage += 5;
+	sp_prev_was_write = 0;
+}
+
 #else
+
 int serprog_init(void)
 {
 	fprintf(stderr, "Serial programmer support was not compiled in\n");

-- 
urjaman
-------------- next part --------------
Serial Flasher Protocol Specification - version 1 (0x01 return value == 1)

Command And Answer Sequence - all commands give an answer.
PC: COMMAND(8bit) <parameters determined by opcode>
DEV: ACK/NAK(8bit) <OPTIONAL RETURN BYTES (only if ACK)> / nothing
Command 0x10 (SYNCNOP) has a special return of NAK+ACK for synchronization.

ACK = 0x06
NAK = 0x15

All multibyte values are little-endian. Addresses and lengths are 24-bit.

COMMAND	Description			Parameters			Return Value
0x00	NOP				none				ACK
0x01	Query programmer iface version	none				ACK + 16bit version (nonzero)
0x02	Query supported commands bitmap	none				ACK + 32 bytes (256 bits) of supported cmds flags
0x03	Query programmer name		none				ACK + 16 bytes string (null padding) / NAK
0x04	Query serial buffer size	none				ACK + 16bit size / NAK
0x05	Query supported bustypes	none				ACK + 8-bit flags (as per flashrom) / NAK
0x06	Query connected address lines	none				ACK + 8bit line count / NAK
0x07	Query operation buffer size	none				ACK + 16bit size / NAK
0x08	Query write-n maximum data len	none				ACK + 24bit maximum length / NAK
0x09	Read byte			24-bit addr			ACK + BYTE / NAK
0x0A	Read n bytes			24-bit addr + 24-bit length	ACK + length bytes / NAK
0x0B	Initialize operation buffer	none				ACK / NAK
0x0C	Write to opbuf: Write byte	24-bit addr + 8-bit byte	ACK / NAK (NOTE: takes 5 bytes in opbuf)
0x0D	Write to opbuf: Write n		24-bit length + 24-bit addr +	ACK / NAK (NOTE: takes 7+n bytes in opbuf)
					 + length bytes of data
0x0E	Write to opbuf: delay		32-bit usecs			ACK / NAK (NOTE: takes 5 bytes in opbuf)
0x0F	Execute operation buffer	none				ACK / NAK
0x10	Sync NOP			none				NAK + ACK (for synchronization)
0x11	Query maximum read-n length	none				ACK + 24-bit length (0==2^24) / NAK
0x12	Set used bustype		8-bit flags (as with 0x05)	ACK / NAK
0x??	unimplemented command - invalid.


Additional information of the above commands:
	About unimplemented commands / startup sequence:
		Only commands allowed to be used without checking anything are 0x00,0x10 and 0x01 (NOP,SYNCNOP,Q_IFACE).
		If 0x01 doesn't return 1, dont do anything if you dont support a newer protocol.
		Then, check support for any other opcode (except 0x02) by using 0x02 (Q_CMDMAP).
	0x02 (Q_CMDMAP):
		The map's bits are mapped as follows:
		cmd 0 support: byte 0 bit 0
		cmd 1 support: byte 0 bit 1
		cmd 7 support: byte 0 bit 7
		cmd 8 support: byte 1 bit 0, and so on.
	0x04 (Q_SERBUF):
		If the programmer has guaranteedly working flow control,
		it should return a big bogus value - eg 0xFFFF.
	0x05 (Q_BUSTYPE):
		The bit's are defined as follows:
		bit 0: PARALLEL, bit 1: LPC, bit 2: FWH, bit 3: SPI (if ever supported).
	0x06 (Q_CHIPSIZE):
		Only applicable to parallel programmers.
		An LPC/FHW/SPI-programmer can report this as not supported in the command bitmap.
	0x08 (Q_WRNMAXLEN):
		If a programmer reports a bigger maximum write-n length than the serial buffer size,
		it is assumed that the programmer can process the data fast enough to take in the
		reported maximum write-n without problems.
	0x0F (O_EXEC):
		Execute operation buffer will also clear it, regardless of the return value.
	0x11 (Q_RDNMAXLEN):
		If this command is not supported, assume return of 0 (2^24).
	0x12 (S_BUSTYPE):
		Set's the used bustype if the programmer can support more than one flash protocol.
		Sending a byte with more than 1 bit set will make the programmer decide among them
		on it's own. Bit values as with Q_BUSTYPE.
	About mandatory commands:
		The only truly mandatory commands for any device are 0x00, 0x01, 0x02 and 0x10,
		but one can't really do anything with these commands.
		Support for the following commands is necessary for flashrom to operate properly:
		S_CMD_Q_SERBUF, S_CMD_Q_OPBUF, S_CMD_Q_WRNMAXLEN, S_CMD_R_BYTE,
		S_CMD_R_NBYTES, S_CMD_O_INIT, S_CMD_O_WRITEB, S_CMD_O_WRITEN,
		S_CMD_O_DELAY, S_CMD_O_EXEC.
		In addition, support for these commands is recommended:
		S_CMD_Q_PGMNAME, S_CMD_Q_BUSTYPE, S_CMD_Q_CHIPSIZE (if parallel).


This define listing should help C coders - (it's here to be the single source for copying - will be a .h someday i think)
#define S_ACK 0x06
#define S_NAK 0x15
#define S_CMD_NOP		0x00            /* No operation                                 */
#define S_CMD_Q_IFACE           0x01            /* Query interface version                      */
#define S_CMD_Q_CMDMAP		0x02		/* Query supported commands bitmap		*/
#define S_CMD_Q_PGMNAME         0x03            /* Query programmer name                        */
#define S_CMD_Q_SERBUF          0x04            /* Query Serial Buffer Size                     */
#define S_CMD_Q_BUSTYPE         0x05            /* Query supported bustypes                     */
#define S_CMD_Q_CHIPSIZE        0x06            /* Query supported chipsize (2^n format)        */
#define S_CMD_Q_OPBUF           0x07            /* Query operation buffer size                  */
#define S_CMD_Q_WRNMAXLEN	0x08		/* Query Write to opbuf: Write-N maximum lenght */
#define S_CMD_R_BYTE            0x09            /* Read a single byte                           */
#define S_CMD_R_NBYTES          0x0A            /* Read n bytes                                 */
#define S_CMD_O_INIT            0x0B            /* Initialize operation buffer                  */
#define S_CMD_O_WRITEB          0x0C            /* Write opbuf: Write byte with address         */
#define S_CMD_O_WRITEN		0x0D		/* Write to opbuf: Write-N			*/
#define S_CMD_O_DELAY           0x0E            /* Write opbuf: udelay                          */
#define S_CMD_O_EXEC            0x0F            /* Execute operation buffer                     */
#define S_CMD_SYNCNOP		0x10		/* Special no-operation that returns NAK+ACK	*/
#define S_CMD_Q_RDNMAXLEN	0x11		/* Query read-n maximum length			*/
#define S_CMD_S_BUSTYPE		0x12		/* Set used bustype(s).				*/
-------------- next part --------------
A non-text attachment was scrubbed...
Name: serprog_flashrom_v4.diff
Type: text/x-patch
Size: 22607 bytes
Desc: not available
URL: <http://www.coreboot.org/pipermail/coreboot/attachments/20090628/5b2c2e19/attachment.diff>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: frser-avr.tar.bz2
Type: application/x-bzip2
Size: 12326 bytes
Desc: not available
URL: <http://www.coreboot.org/pipermail/coreboot/attachments/20090628/5b2c2e19/attachment.bin>


More information about the coreboot mailing list