Support for an external serial flasher protocol. Supports RS-232, USB serial converters (untested) and TCP streams.
Signed-off-by: Urja Rannikko urjaman@gmail.com
--- Please commit my jedec.c readb to readn patch (or carldani's version that i've only heard of in IRC) before this - they're not dependent but without that patch this protocol is awfully slow.
Also attached is a simple specification of the protocol this code implements, it hasnt changed much from my last mail, but i attach it here so it's readily available in the same mail with the implementation.
I also have the implementation of this protocol on the device side for an Atmel AVR ATmega168 microcontroller, it is also included just for an idea of how i implemented the device side parser - it has GPL headers added to all files and COPYING included to make it GPLv2+ for real, but has not otherwise been looked at for release - the device it runs on is in a way very special, so i think compiling that directly would be of little use to anyone atm.
Excuse me, i forgot to inline the patch, and also do remember to svn add serprog.c: the patch: Index: flash.h =================================================================== --- flash.h (revision 580) +++ flash.h (working copy) @@ -85,6 +85,7 @@ #define PROGRAMMER_NIC3COM 0x02 #define PROGRAMMER_SATASII 0x03 #define PROGRAMMER_IT87SPI 0x04 +#define PROGRAMMER_SERPROG 0x05
struct programmer_entry { const char *vendor; @@ -906,4 +907,12 @@ int erase_stm50flw0x0x(struct flashchip *flash); int write_stm50flw0x0x(struct flashchip *flash, uint8_t *buf);
+/* serprog.c */ +extern char* serprog_param; +int serprog_init(void); +int serprog_shutdown(void); +void serprog_chip_writeb(uint8_t val, chipaddr addr); +uint8_t serprog_chip_readb(const chipaddr addr); +void serprog_chip_readn(uint8_t *buf, const chipaddr addr, size_t len); +void serprog_delay(int delay); #endif /* !__FLASH_H__ */ Index: serprog.c =================================================================== --- serprog.c (revision 0) +++ serprog.c (revision 0) @@ -0,0 +1,576 @@ +/* + * This file is part of the flashrom project. + * + * Copyright (C) 2009 Urja Rannikko urjaman@gmail.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <string.h> +#include <stdlib.h> +#include <ctype.h> +#include <fcntl.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <arpa/inet.h> +#include <netinet/in.h> +#include <netinet/tcp.h> +#include <netdb.h> +#include <sys/stat.h> +#include <errno.h> +#include <stdio.h> +#include <unistd.h> +#include <inttypes.h> +#include <termios.h> +#include "flash.h" + +#define MSGHEADER "serprog:" + +#define S_ACK 0x10 +#define S_NAK 0xBA +#define S_CMD_SYNCNOP 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 */ + +int serprog_fd; +char *serprog_param = NULL; + +uint16_t sp_device_serbuf_size = 16; +uint16_t sp_device_opbuf_size = 300; +/* a bit map of supported commands */ +uint8_t sp_cmdmap[32]; + +/* sp_prev_was_write used to detect writes with continouous addresses and combine them to write-n's */ +int sp_prev_was_write = 0; +/* sp_write_n_addr used as the starting addr of the currently combined write-n operation */ +uint32_t sp_write_n_addr; +/* The maximum length of an write_n operation on the device - 0 = not supported at all */ +uint32_t sp_write_n_support = 0; +/* A malloc'd buffer for combining the operation's data and a counter that tells how much data is there */ +uint8_t* sp_write_n_buf; +uint32_t sp_write_n_bytes = 0; + +/* sp_streamed_* used for flow control checking */ +int sp_streamed_transmit_ops = 0; +int sp_streamed_transmit_bytes = 0; +/* sp_opbuf_usage used for counting the amount of on-device operation buffer used */ +int sp_opbuf_usage = 0; +/* if true causes serprog_docommand to automatically check whether the command is supported before doing it */ +int sp_check_avail_automatic = 0; + + +static void serprog_die(char* msg) { + perror(msg); + exit(1); +} + +static int serprog_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) serprog_die("Error: serprog cannot open socket"); + hostPtr = gethostbyname(ip); + if (NULL == hostPtr) { + hostPtr = gethostbyaddr(ip,strlen(ip),AF_INET); + if (NULL == hostPtr) serprog_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); + serprog_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; +}; + +const struct baudentry baudtable[] = { + { B9600, 9600 }, + { B19200, 19200 }, + { B38400, 38400 }, + { B57600, 57600 }, + { B115200, 115200 }, + { 0, 0 } /* Terminator */ +}; + +static int serprog_openserport(char* dev, unsigned int baud) { + struct termios options; + int fd,i; + fd = open(dev,O_RDWR|O_NOCTTY|O_NDELAY); + if (fd<0) serprog_die("Error: cannot open serial port"); + fcntl(fd, F_SETFL, 0); + tcgetattr(fd,&options); + for(i=0;;i++) { + if (baudtable[i].baud == 0) { + close(fd); + fprintf(stderr,"Error: cannot configure for baudrate %d\n",baud); + exit(1); + } + if (baudtable[i].baud == baud) { + cfsetispeed(&options, baudtable[i].flag); + cfsetospeed(&options, 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 serprog_flush_incoming(void) { + for(;;) { + unsigned char flush[16]; + ssize_t rv; + rv = read(serprog_fd,flush,sizeof(flush)); + if ((rv == -1)&&(errno == EAGAIN)) break; + if (rv == -1) serprog_die("flush read"); + } + return; +} + +/* 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 serprog_synchronize(void) { + int i; + int flags = fcntl(serprog_fd,F_GETFL); + unsigned char buf[8]; + flags |= O_NONBLOCK; + fcntl(serprog_fd,F_SETFL,flags); + serprog_flush_incoming(); + printf_debug("Flushed incoming data - now syncing\n"); + /* 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,0,8); + if(write(serprog_fd,buf,8) != 8) serprog_die("flush write"); + usleep(1000 * 1000); /* A second should be enough to get all the answers to the buffer */ + serprog_flush_incoming(); + + /* Then try upto 8 times to send nop and get the correct special return of NAK+ACK */ + for(i=0;i<8;i++) { + int n; + unsigned char c = 0; + if (write(serprog_fd,&c,1) != 1) serprog_die("sync write"); + printf_debug(".") + fflush(stdout); + for(n=0;n<50;n++) { // wait upto 500ms + ssize_t rv; + usleep(10 * 1000); // sleep 10 ms + rv = read(serprog_fd,&c,1); + if ((rv == 1)&&(c == S_NAK)) { + usleep(10 * 1000); /* a careful 10ms to make sure the second byte arrived too */ + rv = read(serprog_fd,&c,1); + if ((rv == 1)&&(c == S_ACK)) { + flags &= ~O_NONBLOCK; + fcntl(serprog_fd,F_SETFL,flags); + return; // our task is done + } + } + } + } + + 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; + if (sp_cmdmap[byteoffs] & (1<<bitoffs)) return 1; + return 0; +} + +static int sp_automatic_cmdcheck(uint8_t cmd) { + if (sp_check_avail_automatic) if (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 serprog_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) serprog_die("Error: cannot malloc command buffer"); + sendpacket[0] = command; + memcpy(&(sendpacket[1]),params,parmlen); + if (write(serprog_fd,sendpacket,1+parmlen) != (1+parmlen)) serprog_die("Error: cannot write command"); + free(sendpacket); + if (read(serprog_fd,&c,1) != 1) serprog_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 = read(serprog_fd,retparms+rd_bytes,retlen-rd_bytes); + if (r <= 0) serprog_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(serprog_fd,&c,1) != 1) serprog_die("Error: cannot read from device (flushing stream)"); + if (c == S_NAK) { + fprintf(stderr,"Error: NAK to some 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) serprog_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(serprog_fd,sp,1+parmlen) != (1+parmlen)) serprog_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) serprog_die("Error: memory allocation failure"); + memcpy(dev,serprog_param,len); + dev[len] = 0; + num = strdup(num+1); + if (!num) serprog_die("Error: memory allocation failure"); + free (serprog_param); + serprog_param = NULL; + + if (dev[0] == '/') serprog_fd = serprog_openserport(dev,atoi(num)); + else serprog_fd = serprog_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; + + serprog_synchronize(); + + printf_debug(MSGHEADER "Synchronized\n"); + + if (serprog_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 (serprog_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 (serprog_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 (serprog_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 (serprog_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 (serprog_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 (serprog_docommand(S_CMD_O_INIT,0,NULL,0,NULL)) { + fprintf(stderr,"Error: NAK to initialize operation buffer\n"); + exit(1); + } + + if (serprog_docommand(S_CMD_Q_WRNMAXLEN,0,NULL,3,rbuf)) { + printf_debug(MSGHEADER "Write-n not supported"); + sp_write_n_support = 0; + } else { + sp_write_n_support = ((unsigned int)(rbuf[0]) << 0); + sp_write_n_support |= ((unsigned int)(rbuf[1]) << 8); + sp_write_n_support |= ((unsigned int)(rbuf[2]) << 16); + printf_debug(MSGHEADER "Maximum write-n length %d\n",sp_write_n_support); + sp_write_n_buf = malloc(sp_write_n_support); + if (!sp_write_n_buf) { + fprintf(stderr,"Error: cannot allocate memory for Write-n buffer\n"); + exit(1); + } + sp_write_n_bytes = 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 on-device operation buffer */ +static void serprog_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(); + if (sp_write_n_bytes == 1) { /* In case it's just a single byte send it as a single write */ + 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 += 6; + 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(serprog_fd,header,7) != 7) serprog_die("Error: cannot write write-n command\n"); + if (write(serprog_fd,sp_write_n_buf,sp_write_n_bytes) != sp_write_n_bytes) serprog_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 serprog_execute_opbuf(void) { + if ((sp_write_n_support)&&(sp_write_n_bytes)) serprog_pass_writen(); + sp_stream_buffer_op(S_CMD_O_EXEC,0,0); + sp_flush_stream(); + printf_debug(MSGHEADER "Executed operation buffer of %d bytes\n",sp_opbuf_usage); + sp_opbuf_usage = 0; + sp_prev_was_write = 0; + return; +} + +int serprog_shutdown(void) { + printf_debug("%s\n", __func__); + if ((sp_opbuf_usage)||(sp_write_n_support&&sp_write_n_bytes)) serprog_execute_opbuf(); + close(serprog_fd); + if (sp_write_n_support) 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)) { + serprog_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_write_n_support) { + 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)) serprog_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_write_n_support) serprog_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 += 6; + } +} + + +uint8_t serprog_chip_readb(const chipaddr addr) { + unsigned char c; + unsigned char buf[3]; + if ((sp_opbuf_usage)||(sp_write_n_support&&sp_write_n_bytes)) serprog_execute_opbuf(); + buf[0] = ((addr >> 0) & 0xFF); + buf[1] = ((addr >> 8) & 0xFF); + buf[2] = ((addr >> 16) & 0xFF); + if (serprog_docommand(S_CMD_R_BYTE,3,buf,1,&c)) { + fprintf(stderr,"Error: NAK to Read byte command\n"); + exit(1); + } + printf_debug("%s addr=0x%lx returning 0x%02X\n", __func__,addr,c); + return c; +} + + +void serprog_chip_readn(uint8_t *buf, const chipaddr addr, size_t len) { + unsigned char sbuf[6]; + printf_debug("%s: addr=0x%lx len=%d\n", __func__,addr,len); + if ((sp_opbuf_usage)||(sp_write_n_support&&sp_write_n_bytes)) serprog_execute_opbuf(); + 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); + if (serprog_docommand(S_CMD_R_NBYTES,6,sbuf,len,buf)) { + fprintf(stderr,"Error: NAK to Read N bytes command\n"); + exit(1); + } + return; +} + +void serprog_delay(int delay) { + unsigned char buf[4]; + printf_debug("%s\n", __func__); + if ((sp_write_n_support)&&(sp_write_n_bytes)) serprog_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; +}
Property changes on: serprog.c ___________________________________________________________________ Name: svn:eol-style + native
Index: Makefile =================================================================== --- Makefile (revision 580) +++ Makefile (working copy) @@ -34,7 +34,7 @@ sst49lfxxxc.o sst_fwhub.o layout.o cbtable.o flashchips.o physmap.o \ flashrom.o w39v080fa.o sharplhf00l04.o w29ee011.o spi.o it87spi.o \ ichspi.o w39v040c.o sb600spi.o wbsio_spi.o m29f002.o internal.o \ - dummyflasher.o pcidev.o nic3com.o satasii.o + dummyflasher.o pcidev.o nic3com.o satasii.o serprog.o
all: pciutils dep $(PROGRAM)
Index: flashrom.c =================================================================== --- flashrom.c (revision 580) +++ flashrom.c (working copy) @@ -114,6 +114,21 @@ .chip_writen = fallback_chip_writen, .delay = internal_delay, }, + { + .init = serprog_init, + .shutdown = serprog_shutdown, + .map_flash_region = fallback_map, + .unmap_flash_region = fallback_unmap, + .chip_readb = serprog_chip_readb, + .chip_readw = fallback_chip_readw, + .chip_readl = fallback_chip_readl, + .chip_readn = serprog_chip_readn, + .chip_writeb = serprog_chip_writeb, + .chip_writew = fallback_chip_writew, + .chip_writel = fallback_chip_writel, + .chip_writen = fallback_chip_writen, + .delay = serprog_delay, + },
{}, }; @@ -631,6 +646,10 @@ pcidev_bdf = strdup(optarg + 8); } else if (strncmp(optarg, "it87spi", 7) == 0) { programmer = PROGRAMMER_IT87SPI; + } else if (strncmp(optarg, "serprog", 7) == 0) { + programmer = PROGRAMMER_SERPROG; + if (optarg[7] == '=') + serprog_param = strdup(optarg + 8); } else { printf("Error: Unknown programmer.\n"); exit(1);
Hi,
from serial-flash-protocol.txt
#define S_ACK 0x10 #define S_NAK 0xBA
Could these be 0x06 and 0x15 respectively so it's more inline with http://en.wikipedia.org/wiki/C0_and_C1_control_codes ?
Greetings, Frieder
On Wed, Jun 10, 2009 at 13:34, Frieder Ferlemannfrieder.ferlemann@web.de wrote:
Hi,
from serial-flash-protocol.txt
#define S_ACK 0x10 #define S_NAK 0xBA
Could these be 0x06 and 0x15 respectively so it's more inline with http://en.wikipedia.org/wiki/C0_and_C1_control_codes ?
Why not but why to change. Is there some technical reason to follow the ASCII control codes in this binary protocol?
Maybe i should have called them S_OK and S_BAD, or S_YES and S_NO, or S_SMILE and S_FROWN :P
They are just two values used to represent success/failure in command execution - i dont really care much of their numerical values.
Technically speaking, the ACK/NAK ASCII codes don't need to be used, but I think that it would be much more approachable for folks implementing the protocol if the traditional ASCII codes were used.
Also, if you are using the NAK+ACK for synchronizing, it might be good to do more than one round, like NAK+ACK+NAK+ACK+NAK+ACK. I just pulled this suggestion out of the air, so maybe rs232 geeks can speak up as to whether it makes sense or not.
It also might make sense to have the NOP for synchronization use some combination of 1's and 0's so that an improper speed configuration on the computer side would be less likely to cause problems. Again, rs232 geeks can tell us of if that suggestion makes sense.
Thanks, wt
On Wed, Jun 10, 2009 at 11:00 AM, Urja Rannikkourjaman@gmail.com wrote:
On Wed, Jun 10, 2009 at 13:34, Frieder Ferlemannfrieder.ferlemann@web.de wrote:
Hi,
from serial-flash-protocol.txt
#define S_ACK 0x10 #define S_NAK 0xBA
Could these be 0x06 and 0x15 respectively so it's more inline with http://en.wikipedia.org/wiki/C0_and_C1_control_codes ?
Why not but why to change. Is there some technical reason to follow the ASCII control codes in this binary protocol?
Maybe i should have called them S_OK and S_BAD, or S_YES and S_NO, or S_SMILE and S_FROWN :P
They are just two values used to represent success/failure in command execution - i dont really care much of their numerical values.
-- urjaman
-- coreboot mailing list: coreboot@coreboot.org http://www.coreboot.org/mailman/listinfo/coreboot
On Wed, Jun 10, 2009 at 23:20, Warren Turkalwt@penguintechs.org wrote:
Technically speaking, the ACK/NAK ASCII codes don't need to be used, but I think that it would be much more approachable for folks implementing the protocol if the traditional ASCII codes were used.
Ok, i'll change those then - not a big issue for me.
Also, if you are using the NAK+ACK for synchronizing, it might be good to do more than one round, like NAK+ACK+NAK+ACK+NAK+ACK. I just pulled this suggestion out of the air, so maybe rs232 geeks can speak up as to whether it makes sense or not.
It might be worth it doing one additional SYNCNOP cycle with a condition that the NAK must be the first byte received (after giving the command) to make sure of the synchronization, but i wont do more than that because this synchronization function is pretty slow already (and kinda udelay-dependent).
It also might make sense to have the NOP for synchronization use some combination of 1's and 0's so that an improper speed configuration on the computer side would be less likely to cause problems. Again, rs232 geeks can tell us of if that suggestion makes sense.
I think that this is not necessary (see next "chapter"), altough i might change the protocol to have command 0x10 as SYNCNOP (returning NAK+ACK) and 0x00 as just NOP - returning ACK, this might improve the robustness of the protocol.
If the receiver samples 0x00 too slowly, it will get a byte with some bits set (in the on-the-line last bits). If the receiver samples 0x00 too fast, it will get 0x00, but also a stop bit error (framing error. stop bit = 0) and should be able to filter it out.
I'll modify all the code and specification and send a new patch (maybe today (this mail is sent 13:20)).
Support for an external serial flasher protocol. Supports RS-232, USB serial converters (untested) and TCP streams.
Signed-off-by: Urja Rannikko urjaman@gmail.com
--- I'm repeating myself, but: Please commit my jedec.c chip_readb to chip_readn patch (or carldani's version that i've only heard of in IRC) before this - they're not dependent but without that patch this protocol is awfully slow.
Also attached is a simple specification of the protocol this code implements, changed values for ACK and NAK, changed SYNCNOP to NOP, added 0x10 = SYNCNOP.
Again, also attached the AVR parser code. Same disclaimers as previously.
Remember to svn add serprog.c if going to commit.
also, patch inlined: Index: flash.h =================================================================== --- flash.h (revision 589) +++ flash.h (working copy) @@ -85,6 +85,7 @@ #define PROGRAMMER_NIC3COM 0x02 #define PROGRAMMER_SATASII 0x03 #define PROGRAMMER_IT87SPI 0x04 +#define PROGRAMMER_SERPROG 0x05
struct programmer_entry { const char *vendor; @@ -918,4 +919,12 @@ int erase_stm50flw0x0x(struct flashchip *flash); int write_stm50flw0x0x(struct flashchip *flash, uint8_t *buf);
+/* serprog.c */ +extern char* serprog_param; +int serprog_init(void); +int serprog_shutdown(void); +void serprog_chip_writeb(uint8_t val, chipaddr addr); +uint8_t serprog_chip_readb(const chipaddr addr); +void serprog_chip_readn(uint8_t *buf, const chipaddr addr, size_t len); +void serprog_delay(int delay); #endif /* !__FLASH_H__ */ Index: serprog.c =================================================================== --- serprog.c (revision 0) +++ serprog.c (revision 0) @@ -0,0 +1,646 @@ +/* + * This file is part of the flashrom project. + * + * Copyright (C) 2009 Urja Rannikko urjaman@gmail.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <string.h> +#include <stdlib.h> +#include <ctype.h> +#include <fcntl.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <arpa/inet.h> +#include <netinet/in.h> +#include <netinet/tcp.h> +#include <netdb.h> +#include <sys/stat.h> +#include <errno.h> +#include <stdio.h> +#include <unistd.h> +#include <inttypes.h> +#include <termios.h> +#include "flash.h" + +#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 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 */ + +int serprog_fd; +char *serprog_param = NULL; + +uint16_t sp_device_serbuf_size = 16; +uint16_t sp_device_opbuf_size = 300; +/* a bit map of supported commands */ +uint8_t sp_cmdmap[32]; + +/* sp_prev_was_write used to detect writes with continouous addresses and combine them to write-n's */ +int sp_prev_was_write = 0; +/* sp_write_n_addr used as the starting addr of the currently combined write-n operation */ +uint32_t sp_write_n_addr; +/* The maximum length of an write_n operation on the device - 0 = not supported at all */ +uint32_t sp_write_n_support = 0; +/* A malloc'd buffer for combining the operation's data and a counter that tells how much data is there */ +uint8_t* sp_write_n_buf; +uint32_t sp_write_n_bytes = 0; + +/* sp_streamed_* used for flow control checking */ +int sp_streamed_transmit_ops = 0; +int sp_streamed_transmit_bytes = 0; +/* sp_opbuf_usage used for counting the amount of on-device operation buffer used */ +int sp_opbuf_usage = 0; +/* if true causes serprog_docommand to automatically check whether the command is supported before doing it */ +int sp_check_avail_automatic = 0; + + +static void serprog_die(char* msg) { + perror(msg); + exit(1); +} + +static int serprog_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) serprog_die("Error: serprog cannot open socket"); + hostPtr = gethostbyname(ip); + if (NULL == hostPtr) { + hostPtr = gethostbyaddr(ip,strlen(ip),AF_INET); + if (NULL == hostPtr) serprog_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); + serprog_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 }, +const struct baudentry 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 serprog_openserport(char* dev, unsigned int baud) { + struct termios options; + int fd,i; + fd = open(dev,O_RDWR|O_NOCTTY|O_NDELAY); + if (fd<0) serprog_die("Error: cannot open serial port"); + fcntl(fd, F_SETFL, 0); + tcgetattr(fd,&options); + for(i=0;;i++) { + if (baudtable[i].baud == 0) { + close(fd); + fprintf(stderr,"Error: cannot configure for baudrate %d\n",baud); + exit(1); + } + if (baudtable[i].baud == baud) { + cfsetispeed(&options, baudtable[i].flag); + cfsetospeed(&options, 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 serprog_flush_incoming(void) { + for(;;) { + unsigned char flush[16]; + ssize_t rv; + rv = read(serprog_fd,flush,sizeof(flush)); + if ((rv == -1)&&(errno == EAGAIN)) break; + if (rv == -1) serprog_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(serprog_fd,&c,1); + if (rv == 1) return c; + if ((rv == -1)&&(errno != EAGAIN)) serprog_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 serprog_synchronize(void) { + int i; + int flags = fcntl(serprog_fd,F_GETFL); + unsigned char buf[8]; + flags |= O_NONBLOCK; + fcntl(serprog_fd,F_SETFL,flags); + serprog_flush_incoming(); + printf_debug("Flushed incoming data - now syncing\n"); + /* 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(serprog_fd,buf,8) != 8) serprog_die("flush write"); + usleep(1000 * 1000); /* A second should be enough to get all the answers to the buffer */ + serprog_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(serprog_fd,&c,1) != 1) serprog_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(serprog_fd,&c,1) != 1) serprog_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(serprog_fd,F_SETFL,flags); + 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; + if (sp_cmdmap[byteoffs] & (1<<bitoffs)) return 1; + return 0; +} + +static int sp_automatic_cmdcheck(uint8_t cmd) { + if (sp_check_avail_automatic) if (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 serprog_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) serprog_die("Error: cannot malloc command buffer"); + sendpacket[0] = command; + memcpy(&(sendpacket[1]),params,parmlen); + if (write(serprog_fd,sendpacket,1+parmlen) != (1+parmlen)) serprog_die("Error: cannot write command"); + free(sendpacket); + if (read(serprog_fd,&c,1) != 1) serprog_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 = read(serprog_fd,retparms+rd_bytes,retlen-rd_bytes); + if (r <= 0) serprog_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(serprog_fd,&c,1) != 1) serprog_die("Error: cannot read from device (flushing stream)"); + if (c == S_NAK) { + fprintf(stderr,"Error: NAK to some 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) serprog_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(serprog_fd,sp,1+parmlen) != (1+parmlen)) serprog_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) serprog_die("Error: memory allocation failure"); + memcpy(dev,serprog_param,len); + dev[len] = 0; + num = strdup(num+1); + if (!num) serprog_die("Error: memory allocation failure"); + free (serprog_param); + serprog_param = NULL; + + if (dev[0] == '/') serprog_fd = serprog_openserport(dev,atoi(num)); + else serprog_fd = serprog_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; + + serprog_synchronize(); + + printf_debug(MSGHEADER "Synchronized\n"); + + if (serprog_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 (serprog_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 (serprog_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 (serprog_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 (serprog_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 (serprog_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 (serprog_docommand(S_CMD_O_INIT,0,NULL,0,NULL)) { + fprintf(stderr,"Error: NAK to initialize operation buffer\n"); + exit(1); + } + + if (serprog_docommand(S_CMD_Q_WRNMAXLEN,0,NULL,3,rbuf)) { + printf_debug(MSGHEADER "Write-n not supported"); + sp_write_n_support = 0; + } else { + sp_write_n_support = ((unsigned int)(rbuf[0]) << 0); + sp_write_n_support |= ((unsigned int)(rbuf[1]) << 8); + sp_write_n_support |= ((unsigned int)(rbuf[2]) << 16); + printf_debug(MSGHEADER "Maximum write-n length %d\n",sp_write_n_support); + sp_write_n_buf = malloc(sp_write_n_support); + if (!sp_write_n_buf) { + fprintf(stderr,"Error: cannot allocate memory for Write-n buffer\n"); + exit(1); + } + sp_write_n_bytes = 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 on-device operation buffer */ +static void serprog_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(); + if (sp_write_n_bytes == 1) { /* In case it's just a single byte send it as a single write */ + 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 += 6; + 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(serprog_fd,header,7) != 7) serprog_die("Error: cannot write write-n command\n"); + if (write(serprog_fd,sp_write_n_buf,sp_write_n_bytes) != sp_write_n_bytes) serprog_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 serprog_execute_opbuf_noflush(void) { + if ((sp_write_n_support)&&(sp_write_n_bytes)) serprog_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 serprog_execute_opbuf(void) { + serprog_execute_opbuf_noflush(); + sp_flush_stream(); +} + +int serprog_shutdown(void) { + printf_debug("%s\n", __func__); + if ((sp_opbuf_usage)||(sp_write_n_support&&sp_write_n_bytes)) serprog_execute_opbuf(); + close(serprog_fd); + if (sp_write_n_support) 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)) { + serprog_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_write_n_support) { + 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)) serprog_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_write_n_support) serprog_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 += 6; + } +} + + +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_write_n_support&&sp_write_n_bytes)) serprog_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(serprog_fd,&c,1) != 1) serprog_die("readb byteread"); + printf_debug("%s addr=0x%lx returning 0x%02X\n", __func__,addr,c); + return c; +} + + +void serprog_chip_readn(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_write_n_support&&sp_write_n_bytes)) serprog_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(serprog_fd,buf+rd_bytes,len-rd_bytes); + if (r <= 0) serprog_die("Error: cannot read read-n data"); + rd_bytes += r; + } while(rd_bytes != len); + return; +} + +void serprog_delay(int delay) { + unsigned char buf[4]; + printf_debug("%s\n", __func__); + if ((sp_write_n_support)&&(sp_write_n_bytes)) serprog_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; +}
Property changes on: serprog.c ___________________________________________________________________ Name: svn:eol-style + native
Index: Makefile =================================================================== --- Makefile (revision 589) +++ Makefile (working copy) @@ -48,7 +48,7 @@ sst49lfxxxc.o sst_fwhub.o layout.o cbtable.o flashchips.o physmap.o \ flashrom.o w39v080fa.o sharplhf00l04.o w29ee011.o spi.o it87spi.o \ ichspi.o w39v040c.o sb600spi.o wbsio_spi.o m29f002.o internal.o \ - dummyflasher.o pcidev.o nic3com.o satasii.o + dummyflasher.o pcidev.o nic3com.o satasii.o serprog.o
all: pciutils dep $(PROGRAM)
Index: flashrom.c =================================================================== --- flashrom.c (revision 589) +++ flashrom.c (working copy) @@ -114,6 +114,21 @@ .chip_writen = fallback_chip_writen, .delay = internal_delay, }, + { + .init = serprog_init, + .shutdown = serprog_shutdown, + .map_flash_region = fallback_map, + .unmap_flash_region = fallback_unmap, + .chip_readb = serprog_chip_readb, + .chip_readw = fallback_chip_readw, + .chip_readl = fallback_chip_readl, + .chip_readn = serprog_chip_readn, + .chip_writeb = serprog_chip_writeb, + .chip_writew = fallback_chip_writew, + .chip_writel = fallback_chip_writel, + .chip_writen = fallback_chip_writen, + .delay = serprog_delay, + },
{}, }; @@ -500,7 +515,7 @@ " -i | --image <name>: only flash image name from flash layout\n" " -L | --list-supported: print supported devices\n" " -p | --programmer <name>: specify the programmer device\n" - " (internal, dummy, nic3com, satasii, it87spi)\n" + " (internal, dummy, nic3com, satasii, it87spi, serprog)\n" " -h | --help: print this help text\n" " -R | --version: print the version (release)\n" "\nYou can specify one of -E, -r, -w, -v or no operation.\n" @@ -658,6 +673,10 @@ pcidev_bdf = strdup(optarg + 8); } else if (strncmp(optarg, "it87spi", 7) == 0) { programmer = PROGRAMMER_IT87SPI; + } else if (strncmp(optarg, "serprog", 7) == 0) { + programmer = PROGRAMMER_SERPROG; + if (optarg[7] == '=') + serprog_param = strdup(optarg + 8); } else { printf("Error: Unknown programmer.\n"); exit(1);
Support for an external serial flasher protocol. Supports RS-232, USB serial converters (untested) and TCP streams.
Signed-off-by: Urja Rannikko urjaman@gmail.com --- Here we go with the third iteration of this patch - now rebased to r598. Also included a version of the AVR "firmware" that can do 746 byte operation buffers (with 1K sram and 224 byte UART receive buffer, 16 byte UART transmit buffer).
patch inlined: Index: flash.h =================================================================== --- flash.h (revision 598) +++ flash.h (working copy) @@ -87,6 +87,7 @@ #define PROGRAMMER_SATASII 0x03 #define PROGRAMMER_IT87SPI 0x04 #define PROGRAMMER_FT2232SPI 0x05 +#define PROGRAMMER_SERPROG 0x06
struct programmer_entry { const char *vendor; @@ -574,4 +575,12 @@ int erase_stm50flw0x0x(struct flashchip *flash); int write_stm50flw0x0x(struct flashchip *flash, uint8_t *buf);
+/* serprog.c */ +extern char* serprog_param; +int serprog_init(void); +int serprog_shutdown(void); +void serprog_chip_writeb(uint8_t val, chipaddr addr); +uint8_t serprog_chip_readb(const chipaddr addr); +void serprog_chip_readn(uint8_t *buf, const chipaddr addr, size_t len); +void serprog_delay(int delay); #endif /* !__FLASH_H__ */ Index: serprog.c =================================================================== --- serprog.c (revision 0) +++ serprog.c (revision 0) @@ -0,0 +1,743 @@ +/* + * This file is part of the flashrom project. + * + * Copyright (C) 2009 Urja Rannikko urjaman@gmail.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <string.h> +#include <stdlib.h> +#include <ctype.h> +#include <fcntl.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <arpa/inet.h> +#include <netinet/in.h> +#include <netinet/tcp.h> +#include <netdb.h> +#include <sys/stat.h> +#include <errno.h> +#include <stdio.h> +#include <unistd.h> +#include <inttypes.h> +#include <termios.h> +#include "flash.h" + +#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 */ + +int serprog_fd; +char *serprog_param = NULL; + +uint16_t sp_device_serbuf_size = 16; +uint16_t sp_device_opbuf_size = 300; +/* a bit map of supported commands */ +uint8_t sp_cmdmap[32]; + +/* sp_prev_was_write used to detect writes with continouous addresses + and combine them to write-n's */ +int sp_prev_was_write = 0; +/* sp_write_n_addr used as the starting addr of the currently + combined write-n operation */ +uint32_t sp_write_n_addr; +/* The maximum length of an write_n operation on the device + 0 = not supported at all */ +uint32_t sp_write_n_support = 0; +/* A malloc'd buffer for combining the operation's data + and a counter that tells how much data is there. */ +uint8_t *sp_write_n_buf; +uint32_t sp_write_n_bytes = 0; + +/* sp_streamed_* used for flow control checking */ +int sp_streamed_transmit_ops = 0; +int sp_streamed_transmit_bytes = 0; +/* sp_opbuf_usage used for counting the amount of + on-device operation buffer used */ +int sp_opbuf_usage = 0; +/* if true causes serprog_docommand to automatically check + whether the command is supported before doing it */ +int sp_check_avail_automatic = 0; + +static void serprog_die(char *msg) +{ + perror(msg); + exit(1); +} + +static int serprog_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) + serprog_die("Error: serprog cannot open socket"); + hostPtr = gethostbyname(ip); + if (NULL == hostPtr) { + hostPtr = gethostbyaddr(ip, strlen(ip), AF_INET); + if (NULL == hostPtr) + serprog_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); + serprog_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 }, +const struct baudentry 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 serprog_openserport(char *dev, unsigned int baud) +{ + struct termios options; + int fd, i; + fd = open(dev, O_RDWR | O_NOCTTY | O_NDELAY); + if (fd < 0) + serprog_die("Error: cannot open serial port"); + fcntl(fd, F_SETFL, 0); + tcgetattr(fd, &options); + for (i = 0;; i++) { + if (baudtable[i].baud == 0) { + close(fd); + fprintf(stderr, + "Error: cannot configure for baudrate %d\n", + baud); + exit(1); + } + if (baudtable[i].baud == baud) { + cfsetispeed(&options, baudtable[i].flag); + cfsetospeed(&options, 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 serprog_flush_incoming(void) +{ + for (;;) { + unsigned char flush[16]; + ssize_t rv; + rv = read(serprog_fd, flush, sizeof(flush)); + if ((rv == -1) && (errno == EAGAIN)) + break; + if (rv == -1) + serprog_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(serprog_fd, &c, 1); + if (rv == 1) + return c; + if ((rv == -1) && (errno != EAGAIN)) + serprog_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 serprog_synchronize(void) +{ + int i; + int flags = fcntl(serprog_fd, F_GETFL); + unsigned char buf[8]; + flags |= O_NONBLOCK; + fcntl(serprog_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(serprog_fd, buf, 8) != 8) + serprog_die("flush write"); + /* A second should be enough to get all the answers to the buffer */ + usleep(1000 * 1000); + serprog_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(serprog_fd, &c, 1) != 1) + serprog_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(serprog_fd, &c, 1) != 1) + serprog_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(serprog_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; + if (sp_cmdmap[byteoffs] & (1 << bitoffs)) + return 1; + return 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 serprog_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) + serprog_die("Error: cannot malloc command buffer"); + sendpacket[0] = command; + memcpy(&(sendpacket[1]), params, parmlen); + if (write(serprog_fd, sendpacket, 1 + parmlen) != (1 + parmlen)) { + serprog_die("Error: cannot write command"); + } + free(sendpacket); + if (read(serprog_fd, &c, 1) != 1) + serprog_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(serprog_fd, retparms + rd_bytes, + retlen - rd_bytes); + if (r <= 0) serprog_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(serprog_fd, &c, 1) != 1) { + serprog_die + ("Error: cannot read from device (flushing stream)"); + } + if (c == S_NAK) { + fprintf(stderr, + "Error: NAK to some 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) serprog_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(serprog_fd, sp, 1 + parmlen) != (1 + parmlen)) + serprog_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) serprog_die("Error: memory allocation failure"); + memcpy(dev, serprog_param, len); + dev[len] = 0; + num = strdup(num + 1); + if (!num) serprog_die("Error: memory allocation failure"); + free(serprog_param); + serprog_param = NULL; + + if (dev[0] == '/') serprog_fd = serprog_openserport(dev, atoi(num)); + else serprog_fd = serprog_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; + + serprog_synchronize(); + + printf_debug(MSGHEADER "Synchronized\n"); + + if (serprog_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 (serprog_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 (serprog_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 (serprog_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 (serprog_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 (serprog_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 (serprog_docommand(S_CMD_O_INIT, 0, NULL, 0, NULL)) { + fprintf(stderr, "Error: NAK to initialize operation buffer\n"); + exit(1); + } + + if (serprog_docommand(S_CMD_Q_WRNMAXLEN, 0, NULL, 3, rbuf)) { + printf_debug(MSGHEADER "Write-n not supported"); + sp_write_n_support = 0; + } else { + sp_write_n_support = ((unsigned int)(rbuf[0]) << 0); + sp_write_n_support |= ((unsigned int)(rbuf[1]) << 8); + sp_write_n_support |= ((unsigned int)(rbuf[2]) << 16); + printf_debug(MSGHEADER "Maximum write-n length %d\n", + sp_write_n_support); + sp_write_n_buf = malloc(sp_write_n_support); + if (!sp_write_n_buf) { + fprintf(stderr, + "Error: cannot allocate memory for Write-n buffer\n"); + exit(1); + } + sp_write_n_bytes = 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 serprog_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 += 6; + 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(serprog_fd, header, 7) != 7) + serprog_die("Error: cannot write write-n command\n"); + if (write(serprog_fd, sp_write_n_buf, sp_write_n_bytes) != + sp_write_n_bytes) + serprog_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 serprog_execute_opbuf_noflush(void) +{ + if ((sp_write_n_support) && (sp_write_n_bytes)) + serprog_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 serprog_execute_opbuf(void) +{ + serprog_execute_opbuf_noflush(); + sp_flush_stream(); +} + +int serprog_shutdown(void) +{ + printf_debug("%s\n", __func__); + if ((sp_opbuf_usage) || (sp_write_n_support && sp_write_n_bytes)) + serprog_execute_opbuf(); + close(serprog_fd); + if (sp_write_n_support) + 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)) { + serprog_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_write_n_support) { + 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)) + serprog_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_write_n_support) + serprog_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 += 6; + } +} + +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_write_n_support && sp_write_n_bytes)) + serprog_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(serprog_fd, &c, 1) != 1) + serprog_die("readb byteread"); + printf_debug("%s addr=0x%lx returning 0x%02X\n", __func__, addr, c); + return c; +} + +void serprog_chip_readn(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_write_n_support && sp_write_n_bytes)) + serprog_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(serprog_fd, buf + rd_bytes, len - rd_bytes); + if (r <= 0) + serprog_die("Error: cannot read read-n data"); + rd_bytes += r; + } while (rd_bytes != len); + return; +} + +void serprog_delay(int delay) +{ + unsigned char buf[4]; + printf_debug("%s\n", __func__); + if ((sp_write_n_support) && (sp_write_n_bytes)) + serprog_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; +}
Property changes on: serprog.c ___________________________________________________________________ Name: svn:eol-style + native
Index: Makefile =================================================================== --- Makefile (revision 598) +++ Makefile (working copy) @@ -49,7 +49,7 @@ sst49lfxxxc.o sst_fwhub.o layout.o cbtable.o flashchips.o physmap.o \ flashrom.o w39v080fa.o sharplhf00l04.o w29ee011.o spi.o it87spi.o \ ichspi.o w39v040c.o sb600spi.o wbsio_spi.o m29f002.o internal.o \ - dummyflasher.o pcidev.o nic3com.o satasii.o ft2232_spi.o + dummyflasher.o pcidev.o nic3com.o satasii.o ft2232_spi.o serprog.o
all: pciutils .features dep $(PROGRAM)
Index: flashrom.c =================================================================== --- flashrom.c (revision 598) +++ flashrom.c (working copy) @@ -131,6 +131,21 @@ .chip_writen = fallback_chip_writen, .delay = internal_delay, }, + { + .init = serprog_init, + .shutdown = serprog_shutdown, + .map_flash_region = fallback_map, + .unmap_flash_region = fallback_unmap, + .chip_readb = serprog_chip_readb, + .chip_readw = fallback_chip_readw, + .chip_readl = fallback_chip_readl, + .chip_readn = serprog_chip_readn, + .chip_writeb = serprog_chip_writeb, + .chip_writew = fallback_chip_writew, + .chip_writel = fallback_chip_writel, + .chip_writen = fallback_chip_writen, + .delay = serprog_delay, + },
{}, }; @@ -605,7 +620,7 @@ " -i | --image <name>: only flash image name from flash layout\n" " -L | --list-supported: print supported devices\n" " -p | --programmer <name>: specify the programmer device\n" - " (internal, dummy, nic3com, satasii, it87spi, ft2232spi)\n" + " (internal, dummy, nic3com, satasii, it87spi, ft2232spi, serprog)\n" " -h | --help: print this help text\n" " -R | --version: print the version (release)\n" "\nYou can specify one of -E, -r, -w, -v or no operation.\n" @@ -765,6 +780,10 @@ programmer = PROGRAMMER_IT87SPI; } else if (strncmp(optarg, "ft2232spi", 9) == 0) { programmer = PROGRAMMER_FT2232SPI; + } else if (strncmp(optarg, "serprog", 7) == 0) { + programmer = PROGRAMMER_SERPROG; + if (optarg[7] == '=') + serprog_param = strdup(optarg + 8); } else { printf("Error: Unknown programmer.\n"); exit(1);
On 17.06.2009 00:32, Urja Rannikko wrote:
Support for an external serial flasher protocol. Supports RS-232, USB serial converters (untested) and TCP streams.
Signed-off-by: Urja Rannikko urjaman@gmail.com
Here we go with the third iteration of this patch - now rebased to r598. Also included a version of the AVR "firmware" that can do 746 byte operation buffers (with 1K sram and 224 byte UART receive buffer, 16 byte UART transmit buffer).
Thanks, I have updated the flashrom.c and Makefile changes and comitted them in r625 together with the flash.h changes and a stubbed implementation of serprog.c. That part is Acked-by: Carl-Daniel Hailfinger c-d.hailfinger.devel.2006@gmx.net
That way, you don't have the burden of updating the glue code anymore and others can check if the include files are available on their platforms.
The remainder of your patch follows (and I hope I can complete my review of it soon).
Regards, Carl-Daniel
Index: flashrom-serprog/serprog.c =================================================================== --- flashrom-serprog/serprog.c (Revision 625) +++ flashrom-serprog/serprog.c (Arbeitskopie) @@ -39,9 +39,717 @@
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 */ + +int serprog_fd; + +uint16_t sp_device_serbuf_size = 16; +uint16_t sp_device_opbuf_size = 300; +/* a bit map of supported commands */ +uint8_t sp_cmdmap[32]; + +/* sp_prev_was_write used to detect writes with continouous addresses + and combine them to write-n's */ +int sp_prev_was_write = 0; +/* sp_write_n_addr used as the starting addr of the currently + combined write-n operation */ +uint32_t sp_write_n_addr; +/* The maximum length of an write_n operation on the device + 0 = not supported at all */ +uint32_t sp_write_n_support = 0; +/* A malloc'd buffer for combining the operation's data + and a counter that tells how much data is there. */ +uint8_t *sp_write_n_buf; +uint32_t sp_write_n_bytes = 0; + +/* sp_streamed_* used for flow control checking */ +int sp_streamed_transmit_ops = 0; +int sp_streamed_transmit_bytes = 0; +/* sp_opbuf_usage used for counting the amount of + on-device operation buffer used */ +int sp_opbuf_usage = 0; +/* if true causes serprog_docommand to automatically check + whether the command is supported before doing it */ +int sp_check_avail_automatic = 0; + +static void serprog_die(char *msg) +{ + perror(msg); + exit(1); +} + +static int serprog_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) + serprog_die("Error: serprog cannot open socket"); + hostPtr = gethostbyname(ip); + if (NULL == hostPtr) { + hostPtr = gethostbyaddr(ip, strlen(ip), AF_INET); + if (NULL == hostPtr) + serprog_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); + serprog_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 }, +const struct baudentry 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 serprog_openserport(char *dev, unsigned int baud) +{ + struct termios options; + int fd, i; + fd = open(dev, O_RDWR | O_NOCTTY | O_NDELAY); + if (fd < 0) + serprog_die("Error: cannot open serial port"); + fcntl(fd, F_SETFL, 0); + tcgetattr(fd, &options); + for (i = 0;; i++) { + if (baudtable[i].baud == 0) { + close(fd); + fprintf(stderr, + "Error: cannot configure for baudrate %d\n", + baud); + exit(1); + } + if (baudtable[i].baud == baud) { + cfsetispeed(&options, baudtable[i].flag); + cfsetospeed(&options, 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 serprog_flush_incoming(void) +{ + for (;;) { + unsigned char flush[16]; + ssize_t rv; + rv = read(serprog_fd, flush, sizeof(flush)); + if ((rv == -1) && (errno == EAGAIN)) + break; + if (rv == -1) + serprog_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(serprog_fd, &c, 1); + if (rv == 1) + return c; + if ((rv == -1) && (errno != EAGAIN)) + serprog_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 serprog_synchronize(void) +{ + int i; + int flags = fcntl(serprog_fd, F_GETFL); + unsigned char buf[8]; + flags |= O_NONBLOCK; + fcntl(serprog_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(serprog_fd, buf, 8) != 8) + serprog_die("flush write"); + /* A second should be enough to get all the answers to the buffer */ + usleep(1000 * 1000); + serprog_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(serprog_fd, &c, 1) != 1) + serprog_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(serprog_fd, &c, 1) != 1) + serprog_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(serprog_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; + if (sp_cmdmap[byteoffs] & (1 << bitoffs)) + return 1; + return 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 serprog_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) + serprog_die("Error: cannot malloc command buffer"); + sendpacket[0] = command; + memcpy(&(sendpacket[1]), params, parmlen); + if (write(serprog_fd, sendpacket, 1 + parmlen) != (1 + parmlen)) { + serprog_die("Error: cannot write command"); + } + free(sendpacket); + if (read(serprog_fd, &c, 1) != 1) + serprog_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(serprog_fd, retparms + rd_bytes, + retlen - rd_bytes); + if (r <= 0) serprog_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(serprog_fd, &c, 1) != 1) { + serprog_die + ("Error: cannot read from device (flushing stream)"); + } + if (c == S_NAK) { + fprintf(stderr, + "Error: NAK to some 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) serprog_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(serprog_fd, sp, 1 + parmlen) != (1 + parmlen)) + serprog_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) serprog_die("Error: memory allocation failure"); + memcpy(dev, serprog_param, len); + dev[len] = 0; + num = strdup(num + 1); + if (!num) serprog_die("Error: memory allocation failure"); + free(serprog_param); + serprog_param = NULL; + + if (dev[0] == '/') serprog_fd = serprog_openserport(dev, atoi(num)); + else serprog_fd = serprog_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; + + serprog_synchronize(); + + printf_debug(MSGHEADER "Synchronized\n"); + + if (serprog_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 (serprog_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 (serprog_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 (serprog_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 (serprog_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 (serprog_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 (serprog_docommand(S_CMD_O_INIT, 0, NULL, 0, NULL)) { + fprintf(stderr, "Error: NAK to initialize operation buffer\n"); + exit(1); + } + + if (serprog_docommand(S_CMD_Q_WRNMAXLEN, 0, NULL, 3, rbuf)) { + printf_debug(MSGHEADER "Write-n not supported"); + sp_write_n_support = 0; + } else { + sp_write_n_support = ((unsigned int)(rbuf[0]) << 0); + sp_write_n_support |= ((unsigned int)(rbuf[1]) << 8); + sp_write_n_support |= ((unsigned int)(rbuf[2]) << 16); + printf_debug(MSGHEADER "Maximum write-n length %d\n", + sp_write_n_support); + sp_write_n_buf = malloc(sp_write_n_support); + if (!sp_write_n_buf) { + fprintf(stderr, + "Error: cannot allocate memory for Write-n buffer\n"); + exit(1); + } + sp_write_n_bytes = 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 serprog_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 += 6; + 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(serprog_fd, header, 7) != 7) + serprog_die("Error: cannot write write-n command\n"); + if (write(serprog_fd, sp_write_n_buf, sp_write_n_bytes) != + sp_write_n_bytes) + serprog_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 serprog_execute_opbuf_noflush(void) +{ + if ((sp_write_n_support) && (sp_write_n_bytes)) + serprog_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 serprog_execute_opbuf(void) +{ + serprog_execute_opbuf_noflush(); + sp_flush_stream(); +} + +int serprog_shutdown(void) +{ + printf_debug("%s\n", __func__); + if ((sp_opbuf_usage) || (sp_write_n_support && sp_write_n_bytes)) + serprog_execute_opbuf(); + close(serprog_fd); + if (sp_write_n_support) + 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)) { + serprog_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_write_n_support) { + 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)) + serprog_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_write_n_support) + serprog_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 += 6; + } +} + +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_write_n_support && sp_write_n_bytes)) + serprog_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(serprog_fd, &c, 1) != 1) + serprog_die("readb byteread"); + printf_debug("%s addr=0x%lx returning 0x%02X\n", __func__, addr, c); + return c; +} + +void serprog_chip_readn(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_write_n_support && sp_write_n_bytes)) + serprog_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(serprog_fd, buf + rd_bytes, len - rd_bytes); + if (r <= 0) + serprog_die("Error: cannot read read-n data"); + rd_bytes += r; + } while (rd_bytes != len); + return; +} + +void serprog_delay(int delay) +{ + unsigned char buf[4]; + printf_debug("%s\n", __func__); + if ((sp_write_n_support) && (sp_write_n_bytes)) + serprog_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");
On 23.06.2009 13:44, Carl-Daniel Hailfinger wrote:
On 17.06.2009 00:32, Urja Rannikko wrote:
Support for an external serial flasher protocol. Supports RS-232, USB serial converters (untested) and TCP streams.
Signed-off-by: Urja Rannikko urjaman@gmail.com
The remainder of your patch follows (and I hope I can complete my review of it soon).
A small update on this: Could you add a man page (flashrom.8) description for the flasher syntax? I'll finish the review of your code once the protocol has been finalized.
Regards, Carl-Daniel
On 14.06.2009 20:23, Urja Rannikko wrote:
Support for an external serial flasher protocol. Supports RS-232, USB serial converters (untested) and TCP streams.
Signed-off-by: Urja Rannikko urjaman@gmail.com
Serial Flasher Protocol Specification - version 1 (0x01 return value == 1)
Command And Answer Sequence - not all commands give an answer
AFAICS all commands in your revised spec give an answer.
PC: COMMAND(8bit) <parameters determined by opcode> DEV: ACK/NAK(8bit) <OPTIONAL RETURN BYTES (only if ACK)> or nothing
ACK = 0x06 NAK = 0x15
All multibyte values are little-endian.
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 eg. 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 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 bit 0: PARALLEL bit 1: LPC bit 2: FWH bit 3: SPI if ever supported
We're missing a "set bustype" command here. Such a command should be sent directly after querying the supported bus types.
0x06 Query supported chip size none ACK + 8bit power of two / NAK
Maybe extend this a bit to say "number of address lines" or somesuch.
0x07 Query operation buffer size none ACK + 16bit size / NAK 0x08 Query write-n maximum data len none ACK + 24bit maximum length / NAK
What about a "read-n maximum data length" command?
0x00 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 6 bytes in opbuf)
I think it takes 5 bytes (1 command, 3 addr, 1 data).
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 - Execute operation buffer will also clear it, regardless of the return value 0x10 Sync NOP none NAK + ACK (for synchronization)
0x?? unimplemented command - dont do, dangerous, check for support with 0x02 before using any opcode exept 0x10, 0x00, 0x01 and 0x02. if 0x01 doesn't return 1, dont do anything if you dont support a newer protocol.
You may want to add the following commands for handling of CE#, OE#, WE#. Read GPIO direction bitfield none ACK + 32 bits / NAK (0 is IN, 1 is OUT) Write GPIO direction bitfield 32 bits ACK / NAK Read GPIO status bitfield none ACK + 32 bits / NAK (status of each GPIO) Write GPIO status bitfield 32 bits ACK / NAK (bits with direction=IN are ignored) The GPIO stuff is pretty much optional for the first version of the spec, though.
I have no idea how fast the AVR is. If the AVR is fast enough to actually cause timing problems for Parallel flash with 120 nanosecond timing, we may have to handle this in some way, but then again, we have no way to query chip speed, so it's a moot point.
Other than that, I think the protocol is fine for parallel flash.
Overall, I'd say it is a pretty extensible and roubust spec.
We should ship this as serprog.txt in the source tree.
Regards, Carl-Daniel
I'm replying from my phone with opera mini, so sorry bad formatting/short text.
On 2009-06-23, Carl-Daniel Hailfinger c-d.hailfinger.devel.2006@gmx.net wrote:
On 14.06.2009 20:23, Urja Rannikko wrote:
Support for an external serial flasher protocol. Supports RS-232, USB serial converters (untested) and TCP streams.
Signed-off-by: Urja Rannikko urjaman@gmail.com
Serial Flasher Protocol Specification - version 1 (0x01 return value == 1)
Command And Answer Sequence - not all commands give an answer
AFAICS all commands in your revised spec give an answer.
will fix
PC: COMMAND(8bit) <parameters determined by opcode> DEV: ACK/NAK(8bit) <OPTIONAL RETURN BYTES (only if ACK)> or nothing
ACK = 0x06 NAK = 0x15
All multibyte values are little-endian.
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 eg. 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 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 bit 0: PARALLEL bit 1: LPC bit 2: FWH bit 3: SPI if ever supported
We're missing a "set bustype" command here. Such a command should be sent directly after querying the supported bus types.
i already mentioned that somewhere, will add (the flash driver will need access to the cur flashchip struct for this to work)
0x06 Query supported chip size none ACK + 8bit power of two / NAK
Maybe extend this a bit to say "number of address lines" or somesuch.
Ok
0x07 Query operation buffer size none ACK + 16bit size / NAK 0x08 Query write-n maximum data len none ACK + 24bit maximum length / NAK
What about a "read-n maximum data length" command?
i dont know if necessary - the avr can stream the entire 24bit address space, this doesnt need ram to do - but could add "just in case".
0x00 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 6 bytes in opbuf)
I think it takes 5 bytes (1 command, 3 addr, 1 data).
hm - current avr code saves codespace by encoding all writes in the same len=1-256 format - can change
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 - Execute operation buffer will also clear it, regardless of the return value 0x10 Sync NOP none NAK + ACK (for synchronization)
0x?? unimplemented command - dont do, dangerous, check for support with 0x02 before using any opcode exept 0x10, 0x00, 0x01 and 0x02. if 0x01 doesn't return 1, dont do anything if you dont support a newer protocol.
You may want to add the following commands for handling of CE#, OE#, WE#. Read GPIO direction bitfield none ACK + 32 bits / NAK (0 is IN, 1 is OUT) Write GPIO direction bitfield 32 bits ACK / NAK Read GPIO status bitfield none ACK + 32 bits / NAK (status of each GPIO) Write GPIO status bitfield 32 bits ACK / NAK (bits with direction=IN are ignored) The GPIO stuff is pretty much optional for the first version of the spec, though.
one would need to define the GPIO's too. I think we can add these later if/when needed. The command support bitmap is there just for this kind of stuff.
I have no idea how fast the AVR is. If the AVR is fast enough to actually cause timing problems for Parallel flash with 120 nanosecond timing, we may have to handle this in some way, but then again, we have no way to query chip speed, so it's a moot point.
It depends. Maximum speed for an atmega{8,16,32}8 is 20mhz giving 50ns for the fastest possible operation (operating the shift regs makes things much slower for my board). The AVR code must just have the correct amount of nop's in the read/write cycle code - at 20mhz 2 nops = at best 150ns timing, my code has 2 nops now btw (the AVR runs at 18.432mhz for good serial though) .
Other than that, I think the protocol is fine for parallel flash.
Overall, I'd say it is a pretty extensible and roubust spec.
We should ship this as serprog.txt in the source tree.
ok :)
Regards, Carl-Daniel
Hi all,
Here comes a new version, i added some stuff too. Oh and btw, could we add A/A mux bit (on LPC/FWH chips) to the supported bustypes variable?
This version should be "binary compatible" with the previous spec version, only clarifications and added commands, i will need to add support for read-n maximum length to flashrom though.
I have 6 mins of battery on this laptop so here goes, have to go. <duh> here my battery monitor failed me, but luckily gmail saved the message just in time (opera mini sending again then).
Hi Urja,
I've just seen that 128MBit serial flash memories with SPI interface (like M25P128) seem to exist. I don't know if/when devices of this type will find their way into motherboards, but these devices would already today reach the limits of serial-flash-protocol.txt (24 bit address and length fields).
Could you add a reserved (and for the foreseeable future ignored) byte to these fields or directly use 32 bit?
Greetings, Frieder
On Sun, Jun 28, 2009 at 15:25, Frieder Ferlemannfrieder.ferlemann@web.de wrote:
Hi Urja,
I've just seen that 128MBit serial flash memories with SPI interface (like M25P128) seem to exist. I don't know if/when devices of this type will find their way into motherboards, but these devices would already today reach the limits of serial-flash-protocol.txt (24 bit address and length fields).
Could you add a reserved (and for the foreseeable future ignored) byte to these fields or directly use 32 bit?
I've tried to keep the protocol overhead as minimal as possible, this made me select 3-byte addresses and lengths. The protocol is extensible as in that it's possible to add read n extended (or the like) operation(s) later on, if this is needed. As you might have noticed, if i understood correctly, the M25P128 also reached the limit of the standard motherboard SPI flash memory size (24 bits, that's part of the reason why i chose 24 bits, no motherboard can address more).
I'd like to have some more input on this (somebody else), as I'd hate to slow down the protocol just in case somebody makes 256-Mbit SPI 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)
Support for an external serial flasher protocol. Supports RS-232, USB serial converters (untested) and TCP streams.
Signed-off-by: Urja Rannikko urjaman@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");
On Sun, Jun 28, 2009 at 18:49, Urja Rannikkourjaman@gmail.com wrote:
Support for an external serial flasher protocol. Supports RS-232, USB serial converters (untested) and TCP streams.
Signed-off-by: Urja Rannikko urjaman@gmail.com
---
A small update - compiles without warnings on x86-64 now (printf of an size_t changed to use %lu and cast the size_t to unsigned long). Also made sure the serial port input doesnt mangle the CR/NL bytes.
Patch inlined: Index: serprog.c =================================================================== --- serprog.c (revision 643) +++ 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 | ICRNL | IGNCR | INLCR); + 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=%lu\n", __func__, addr, (unsigned long)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");
On Fri, Jul 10, 2009 at 3:56 PM, Urja Rannikkourjaman@gmail.com wrote:
- printf_debug("%s: addr=0x%lx len=%lu\n", __func__, addr, (unsigned long)len);
I'm almost certain that it'd be better to user %z in the printf string here instead of casting to unsigned long and using %lu in the printf sting.
Also, I think it'd be nice to see a major reduction in the number of casts used in the code if possible. Casts have a real potential to introduce really subtle errors into the code.
Thanks, wt
On 11.07.2009 00:56, Urja Rannikko wrote:
On Sun, Jun 28, 2009 at 18:49, Urja Rannikkourjaman@gmail.com wrote:
Support for an external serial flasher protocol. Supports RS-232, USB serial converters (untested) and TCP streams.
Signed-off-by: Urja Rannikko urjaman@gmail.com
Thank you for reworking the code multiple times in response to reviews.
Acked-by: Carl-Daniel Hailfinger c-d.hailfinger.devel.2006@gmx.net and committed in r656.
Regards, Carl-Daniel
Urja Rannikko wrote:
You may want to add the following commands for handling of CE#, OE#, WE#. .. GPIO ..
one would need to define the GPIO's too.
Keep them out of the protocol. These signals must not be messed with at this high level, they are all in the programmer domain.
//Peter