Patchwork [3/5] Introduce check_trans().

login
register
about
Submitter Stefan Tauner
Date 2012-10-03 04:13:03
Message ID <1349237585-3904-4-git-send-email-stefan.tauner@student.tuwien.ac.at>
Download mbox | patch
Permalink /patch/3769/
State New
Headers show

Comments

Stefan Tauner - 2012-10-03 04:13:03
Adds the infrastructure parts that allow for sanity checks of
transactions without executing them. This includes new fields in
struct spi_programmer and default implementations as well as
concrete implementations for existing programmer where it makes sense.
Some additional cleanups are done as a bonus where i took the
opportunity, namely in sb600 and serprog.

Signed-off-by: Stefan Tauner <stefan.tauner@student.tuwien.ac.at>
---
 bitbang_spi.c   |    1 +
 buspirate_spi.c |   14 +++++++++++++-
 dediprog.c      |   24 ++++++++++++++++--------
 dummyflasher.c  |    1 +
 ft2232_spi.c    |   14 +++++++++++++-
 ichspi.c        |    9 +++++++++
 it85spi.c       |    1 +
 it87spi.c       |   31 +++++++++++++++++++++++--------
 linux_spi.c     |   31 +++++++++++++++++++++++--------
 programmer.h    |    9 +++++++++
 sb600spi.c      |   51 ++++++++++++++++++++++++++++-----------------------
 serprog.c       |   32 +++++++++++++++++++++-----------
 spi.c           |    9 +++++++--
 wbsio_spi.c     |   10 ++++++++++
 14 files changed, 175 insertions(+), 62 deletions(-)

Patch

diff --git a/bitbang_spi.c b/bitbang_spi.c
index 11d2de1..4b9f83a 100644
--- a/bitbang_spi.c
+++ b/bitbang_spi.c
@@ -67,6 +67,7 @@  static const struct spi_programmer spi_programmer_bitbang = {
 	.type		= SPI_CONTROLLER_BITBANG,
 	.max_data_read	= MAX_DATA_READ_UNLIMITED,
 	.max_data_write	= MAX_DATA_WRITE_UNLIMITED,
+	.check_trans	= default_spi_check_trans,
 	.command	= bitbang_spi_send_command,
 	.multicommand	= default_spi_send_multicommand,
 	.read		= default_spi_read,
diff --git a/buspirate_spi.c b/buspirate_spi.c
index 054b4ff..340091c 100644
--- a/buspirate_spi.c
+++ b/buspirate_spi.c
@@ -129,6 +129,14 @@  static int buspirate_wait_for_string(unsigned char *buf, char *key)
 	return ret;
 }
 
+static int buspirate_spi_check_trans(struct flashctx *flash, unsigned int writecnt, unsigned int readcnt,
+				  const unsigned char *writearr)
+{
+	if (writecnt > 16 || readcnt > 16 || (readcnt + writecnt) > 16)
+		return SPI_INVALID_LENGTH;
+	return 0;
+}
+
 static int buspirate_spi_send_command(struct flashctx *flash, unsigned int writecnt, unsigned int readcnt,
 				      const unsigned char *writearr, unsigned char *readarr);
 
@@ -136,6 +144,7 @@  static const struct spi_programmer spi_programmer_buspirate = {
 	.type		= SPI_CONTROLLER_BUSPIRATE,
 	.max_data_read	= 12,
 	.max_data_write	= 12,
+	.check_trans	= buspirate_spi_check_trans,
 	.command	= buspirate_spi_send_command,
 	.multicommand	= default_spi_send_multicommand,
 	.read		= default_spi_read,
@@ -421,8 +430,11 @@  static int buspirate_spi_send_command(struct flashctx *flash,
 	unsigned int i = 0;
 	int ret = 0;
 
-	if (writecnt > 16 || readcnt > 16 || (readcnt + writecnt) > 16)
+	if (buspirate_spi_check_trans(flash, writecnt, readcnt, writearr) != 0) {
+		msg_pspew("%s called with an unsupported transaction layout: readcnt = %d, writecnt = %d.\n",
+			  __func__, readcnt, writecnt);
 		return SPI_INVALID_LENGTH;
+	}
 
 	/* 3 bytes extra for CS#, len, CS#. */
 	if (buspirate_commbuf_grow(writecnt + readcnt + 3))
diff --git a/dediprog.c b/dediprog.c
index a81cf83..643b324 100644
--- a/dediprog.c
+++ b/dediprog.c
@@ -439,6 +439,18 @@  static int dediprog_spi_write_aai(struct flashctx *flash, uint8_t *buf, unsigned
 	return dediprog_spi_write(flash, buf, start, len, DEDI_SPI_CMD_AAIWRITE);
 }
 
+static int dediprog_spi_check_trans(struct flashctx *flash, unsigned int writecnt, unsigned int readcnt,
+				    const unsigned char *writearr)
+{
+	/* Paranoid, but I don't want to be blamed if anything explodes. */
+	if (writecnt > 16)
+		return 1;
+	/* 16 byte reads should work. */
+	if (readcnt > 16)
+		return -1;
+	return 0;
+}
+
 static int dediprog_spi_send_command(struct flashctx *flash,
 				     unsigned int writecnt,
 				     unsigned int readcnt,
@@ -448,14 +460,9 @@  static int dediprog_spi_send_command(struct flashctx *flash,
 	int ret;
 
 	msg_pspew("%s, writecnt=%i, readcnt=%i\n", __func__, writecnt, readcnt);
-	/* Paranoid, but I don't want to be blamed if anything explodes. */
-	if (writecnt > 16) {
-		msg_perr("Untested writecnt=%i, aborting.\n", writecnt);
-		return 1;
-	}
-	/* 16 byte reads should work. */
-	if (readcnt > 16) {
-		msg_perr("Untested readcnt=%i, aborting.\n", readcnt);
+	if (dediprog_spi_check_trans(flash, writecnt, readcnt, writearr) != 0) {
+		msg_perr("%s called with an unsupported transaction layout: readcnt = %d, writecnt = %d.\n",
+			  __func__, readcnt, writecnt);
 		return 1;
 	}
 	
@@ -740,6 +747,7 @@  static const struct spi_programmer spi_programmer_dediprog = {
 	.type		= SPI_CONTROLLER_DEDIPROG,
 	.max_data_read	= MAX_DATA_UNSPECIFIED,
 	.max_data_write	= MAX_DATA_UNSPECIFIED,
+	.check_trans	= dediprog_spi_check_trans,
 	.command	= dediprog_spi_send_command,
 	.multicommand	= default_spi_send_multicommand,
 	.read		= dediprog_spi_read,
diff --git a/dummyflasher.c b/dummyflasher.c
index 655b678..4e08386 100644
--- a/dummyflasher.c
+++ b/dummyflasher.c
@@ -123,6 +123,7 @@  static const struct spi_programmer spi_programmer_dummyflasher = {
 	.type		= SPI_CONTROLLER_DUMMY,
 	.max_data_read	= MAX_DATA_READ_UNLIMITED,
 	.max_data_write	= MAX_DATA_UNSPECIFIED,
+	.check_trans	= default_spi_check_trans, /* FIXME */
 	.command	= dummy_spi_send_command,
 	.multicommand	= default_spi_send_multicommand,
 	.read		= default_spi_read,
diff --git a/ft2232_spi.c b/ft2232_spi.c
index 31a6c5c..d139886 100644
--- a/ft2232_spi.c
+++ b/ft2232_spi.c
@@ -139,6 +139,14 @@  static int get_buf(struct ftdi_context *ftdic, const unsigned char *buf,
 	return 0;
 }
 
+static int ft2232_spi_check_trans(struct flashctx *flash, unsigned int writecnt, unsigned int readcnt,
+				  const unsigned char *writearr)
+{
+	if (writecnt > 65536 || readcnt > 65536)
+		return SPI_INVALID_LENGTH;
+	return 0;
+}
+
 static int ft2232_spi_send_command(struct flashctx *flash,
 				   unsigned int writecnt, unsigned int readcnt,
 				   const unsigned char *writearr,
@@ -148,6 +156,7 @@  static const struct spi_programmer spi_programmer_ft2232 = {
 	.type		= SPI_CONTROLLER_FT2232,
 	.max_data_read	= 64 * 1024,
 	.max_data_write	= 256,
+	.check_trans	= ft2232_spi_check_trans,
 	.command	= ft2232_spi_send_command,
 	.multicommand	= default_spi_send_multicommand,
 	.read		= default_spi_read,
@@ -407,8 +416,11 @@  static int ft2232_spi_send_command(struct flashctx *flash,
 	int bufsize;
 	static int oldbufsize = 0;
 
-	if (writecnt > 65536 || readcnt > 65536)
+	if (ft2232_spi_check_trans(flash, writecnt, readcnt, writearr) != 0) {
+		msg_pspew("%s called with an unsupported transaction layout: readcnt = %d, writecnt = %d.\n",
+			  __func__, readcnt, writecnt);
 		return SPI_INVALID_LENGTH;
+	}
 
 	/* buf is not used for the response from the chip. */
 	bufsize = max(writecnt + 9, 260 + 9);
diff --git a/ichspi.c b/ichspi.c
index e60913c..0ce9dc2 100644
--- a/ichspi.c
+++ b/ichspi.c
@@ -1082,6 +1082,12 @@  static int prepare_opcode(struct flashctx *flash, unsigned int writecnt, unsigne
 	return 0;
 }
 
+static int ich_spi_check_trans(struct flashctx *flash, unsigned int writecnt, unsigned int readcnt,
+			       const unsigned char *writearr)
+{
+	return prepare_opcode(flash, writecnt, readcnt, writearr, true, NULL);
+}
+
 static int ich_spi_send_command(struct flashctx *flash, unsigned int writecnt,
 				unsigned int readcnt,
 				const unsigned char *writearr,
@@ -1563,6 +1569,7 @@  static const struct spi_programmer spi_programmer_ich7 = {
 	.type = SPI_CONTROLLER_ICH7,
 	.max_data_read = 64,
 	.max_data_write = 64,
+	.check_trans	= ich_spi_check_trans,
 	.command = ich_spi_send_command,
 	.multicommand = ich_spi_send_multicommand,
 	.read = default_spi_read,
@@ -1574,6 +1581,7 @@  static const struct spi_programmer spi_programmer_ich9 = {
 	.type = SPI_CONTROLLER_ICH9,
 	.max_data_read = 64,
 	.max_data_write = 64,
+	.check_trans	= ich_spi_check_trans,
 	.command = ich_spi_send_command,
 	.multicommand = ich_spi_send_multicommand,
 	.read = default_spi_read,
@@ -1881,6 +1889,7 @@  static const struct spi_programmer spi_programmer_via = {
 	.type = SPI_CONTROLLER_VIA,
 	.max_data_read = 16,
 	.max_data_write = 16,
+	.check_trans	= ich_spi_check_trans,
 	.command = ich_spi_send_command,
 	.multicommand = ich_spi_send_multicommand,
 	.read = default_spi_read,
diff --git a/it85spi.c b/it85spi.c
index 0b074eb..67c7185 100644
--- a/it85spi.c
+++ b/it85spi.c
@@ -280,6 +280,7 @@  static const struct spi_programmer spi_programmer_it85xx = {
 	.type		= SPI_CONTROLLER_IT85XX,
 	.max_data_read	= 64,
 	.max_data_write	= 64,
+	.check_trans	= default_spi_check_trans,
 	.command	= it85xx_spi_send_command,
 	.multicommand	= default_spi_send_multicommand,
 	.read		= default_spi_read,
diff --git a/it87spi.c b/it87spi.c
index a35ddc0..f7b2a9e 100644
--- a/it87spi.c
+++ b/it87spi.c
@@ -104,6 +104,8 @@  void probe_superio_ite(void)
 	return;
 }
 
+static int it8716f_spi_check_trans(struct flashctx *flash, unsigned int writecnt, unsigned int readcnt,
+				   const unsigned char *writearr);
 static int it8716f_spi_send_command(struct flashctx *flash,
 				    unsigned int writecnt, unsigned int readcnt,
 				    const unsigned char *writearr,
@@ -117,6 +119,7 @@  static const struct spi_programmer spi_programmer_it87xx = {
 	.type		= SPI_CONTROLLER_IT87XX,
 	.max_data_read	= MAX_DATA_UNSPECIFIED,
 	.max_data_write	= MAX_DATA_UNSPECIFIED,
+	.check_trans	= it8716f_spi_check_trans,
 	.command	= it8716f_spi_send_command,
 	.multicommand	= default_spi_send_multicommand,
 	.read		= it8716f_spi_chip_read,
@@ -242,6 +245,18 @@  int init_superio_ite(void)
 	return ret;
 }
 
+static int it8716f_spi_check_trans(struct flashctx *flash, unsigned int writecnt, unsigned int readcnt,
+				   const unsigned char *writearr)
+{
+	if (readcnt > 3) {
+		return -1;
+	}
+	if (writecnt != 1 && writecnt != 2 && writecnt != 4 && writecnt != 5) {
+		return 1;
+	}
+	return 0;
+}
+
 /*
  * The IT8716F only supports commands with length 1,2,4,5 bytes including
  * command byte and can not read more than 3 bytes from the device.
@@ -259,14 +274,16 @@  static int it8716f_spi_send_command(struct flashctx *flash,
 	uint8_t busy, writeenc;
 	int i;
 
+	if (it8716f_spi_check_trans(flash, writecnt, readcnt, writearr) != 0) {
+		msg_pspew("%s called with an unsupported transaction layout: readcnt = %d, writecnt = %d.\n",
+			  __func__, readcnt, writecnt);
+		return SPI_INVALID_LENGTH;
+	}
+
 	do {
 		busy = INB(it8716f_flashport) & 0x80;
 	} while (busy);
-	if (readcnt > 3) {
-		msg_pinfo("%s called with unsupported readcnt %i.\n",
-			  __func__, readcnt);
-		return SPI_INVALID_LENGTH;
-	}
+
 	switch (writecnt) {
 	case 1:
 		OUTB(writearr[0], it8716f_flashport + 1);
@@ -292,9 +309,7 @@  static int it8716f_spi_send_command(struct flashctx *flash,
 		OUTB(writearr[4], it8716f_flashport + 7);
 		writeenc = 0x3;
 		break;
-	default:
-		msg_pinfo("%s called with unsupported writecnt %i.\n",
-			  __func__, writecnt);
+	default: /* Should never happen due to the checks in it8716f_spi_check_trans() */
 		return SPI_INVALID_LENGTH;
 	}
 	/*
diff --git a/linux_spi.c b/linux_spi.c
index 2f46463..2301b84 100644
--- a/linux_spi.c
+++ b/linux_spi.c
@@ -37,6 +37,8 @@ 
 static int fd = -1;
 
 static int linux_spi_shutdown(void *data);
+static int linux_spi_check_trans(struct flashctx *flash, unsigned int writecnt, unsigned int readcnt,
+				 const unsigned char *writearr);
 static int linux_spi_send_command(struct flashctx *flash, unsigned int writecnt,
 				  unsigned int readcnt,
 				  const unsigned char *txbuf,
@@ -50,6 +52,7 @@  static const struct spi_programmer spi_programmer_linux = {
 	.type		= SPI_CONTROLLER_LINUX,
 	.max_data_read	= MAX_DATA_UNSPECIFIED, /* TODO? */
 	.max_data_write	= MAX_DATA_UNSPECIFIED, /* TODO? */
+	.check_trans	= linux_spi_check_trans,
 	.command	= linux_spi_send_command,
 	.multicommand	= default_spi_send_multicommand,
 	.read		= linux_spi_read,
@@ -129,29 +132,41 @@  static int linux_spi_shutdown(void *data)
 	return 0;
 }
 
+static int linux_spi_check_trans(struct flashctx *flash, unsigned int writecnt, unsigned int readcnt,
+				 const unsigned char *writearr)
+{
+	/* The implementation currently does not support requests that
+	   don't start with sending a command. */
+	if (writecnt == 0)
+		return SPI_INVALID_LENGTH;
+	return 0;
+}
+
 static int linux_spi_send_command(struct flashctx *flash, unsigned int writecnt,
 				  unsigned int readcnt,
-				  const unsigned char *txbuf,
-				  unsigned char *rxbuf)
+				  const unsigned char *writearr,
+				  unsigned char *readarr)
 {
 	int iocontrol_code;
 	struct spi_ioc_transfer msg[2] = {
 		{
-			.tx_buf = (uint64_t)(ptrdiff_t)txbuf,
+			.tx_buf = (uint64_t)(ptrdiff_t)writearr,
 			.len = writecnt,
 		},
 		{
-			.rx_buf = (uint64_t)(ptrdiff_t)rxbuf,
+			.rx_buf = (uint64_t)(ptrdiff_t)readarr,
 			.len = readcnt,
 		},
 	};
 
+	int ret = linux_spi_check_trans(flash, writecnt, readcnt, writearr);
+	if (ret != 0)
+		msg_perr("%s called with an unsupported transaction layout: readcnt = %d, writecnt = %d.\n",
+			  __func__, readcnt, writecnt);
+		return ret;
+
 	if (fd == -1)
 		return -1;
-	/* The implementation currently does not support requests that
-	   don't start with sending a command. */
-	if (writecnt == 0)
-		return SPI_INVALID_LENGTH;
 
 	/* Just submit the first (write) request in case there is nothing
 	   to read. Otherwise submit both requests. */
diff --git a/programmer.h b/programmer.h
index dedec67..bcd9547 100644
--- a/programmer.h
+++ b/programmer.h
@@ -529,9 +529,18 @@  struct spi_programmer {
 	int (*read)(struct flashctx *flash, uint8_t *buf, unsigned int start, unsigned int len);
 	int (*write_256)(struct flashctx *flash, uint8_t *buf, unsigned int start, unsigned int len);
 	int (*write_aai)(struct flashctx *flash, uint8_t *buf, unsigned int start, unsigned int len);
+	/* Returns 0 if the supplied data can be sent by this programmer. Possible error codes include:
+	 * SPI_INVALID_LENGTH	if readcnt, writecnt or their combination is not allowed
+	 * negative values	if readcnt is too big
+	 * positive values	if writecnt is too big */
+	int (*check_trans)(struct flashctx *flash, unsigned int writecnt, unsigned int readcnt,
+		   const unsigned char *writearr);
+	int (*check_transs)(struct flashctx *flash, struct spi_command *cmds);
 	const void *data;
 };
 
+int default_spi_check_trans(struct flashctx *flash, unsigned int writecnt, unsigned int readcnt,
+	   const unsigned char *writearr);
 int default_spi_send_command(struct flashctx *flash, unsigned int writecnt, unsigned int readcnt,
 			     const unsigned char *writearr, unsigned char *readarr);
 int default_spi_send_multicommand(struct flashctx *flash, struct spi_command *cmds);
diff --git a/sb600spi.c b/sb600spi.c
index fe60aa9..9acf476 100644
--- a/sb600spi.c
+++ b/sb600spi.c
@@ -89,33 +89,37 @@  static void execute_command(void)
 		;
 }
 
+static int sb600_spi_check_trans(struct flashctx *flash, unsigned int writecnt, unsigned int readcnt,
+				 const unsigned char *writearr)
+{
+	/* We expect 1 opcode byte plus up to 8 data bytes (in any direction):
+	 * the opcode has its own register, the data bytes are sent via an 8 byte FIFO. */
+	if ((writecnt == 0) || (writecnt - 1 + readcnt > 8)) {
+		return SPI_INVALID_LENGTH;
+	}
+	return 0;
+}
+
+
 static int sb600_spi_send_command(struct flashctx *flash, unsigned int writecnt,
 				  unsigned int readcnt,
 				  const unsigned char *writearr,
 				  unsigned char *readarr)
 {
 	int count;
-	/* First byte is cmd which can not being sent through FIFO. */
-	unsigned char cmd = *writearr++;
+	unsigned char cmd = *writearr;
 	unsigned int readoffby1;
 	unsigned char readwrite;
 
-	writecnt--;
-
-	msg_pspew("%s, cmd=%x, writecnt=%x, readcnt=%x\n",
-		  __func__, cmd, writecnt, readcnt);
-
-	if (readcnt > 8) {
-		msg_pinfo("%s, SB600 SPI controller can not receive %d bytes, "
-		       "it is limited to 8 bytes\n", __func__, readcnt);
+	if (sb600_spi_check_trans(flash, writecnt, readcnt, writearr) != 0) {
+		msg_perr("%s called with an unsupported transaction layout: readcnt = %d, writecnt = %d.\n",
+			  __func__, readcnt, writecnt);
 		return SPI_INVALID_LENGTH;
 	}
 
-	if (writecnt > 8) {
-		msg_pinfo("%s, SB600 SPI controller can not send %d bytes, "
-		       "it is limited to 8 bytes\n", __func__, writecnt);
-		return SPI_INVALID_LENGTH;
-	}
+	/* First byte is cmd which can not be sent through FIFO. */
+	writecnt--;
+	writearr++;
 
 	/* This is a workaround for a bug in SB600 and SB700. If we only send
 	 * an opcode and no additional data/address, the SPI controller will
@@ -123,7 +127,7 @@  static int sb600_spi_send_command(struct flashctx *flash, unsigned int writecnt,
 	 * the chip response is discarded and will not end up in the FIFO.
 	 * It is unclear if the CS# line is set high too early as well.
 	 */
-	readoffby1 = (writecnt) ? 0 : 1;
+	readoffby1 = (writecnt == 0) ? 1 : 0;
 	readwrite = (readcnt + readoffby1) << 4 | (writecnt);
 	mmio_writeb(readwrite, sb600_spibar + 1);
 	mmio_writeb(cmd, sb600_spibar + 0);
@@ -196,13 +200,14 @@  static int sb600_spi_send_command(struct flashctx *flash, unsigned int writecnt,
 
 static const struct spi_programmer spi_programmer_sb600 = {
 	.type = SPI_CONTROLLER_SB600,
-	.max_data_read = 8,
-	.max_data_write = 5,
-	.command = sb600_spi_send_command,
-	.multicommand = default_spi_send_multicommand,
-	.read = default_spi_read,
-	.write_256 = default_spi_write_256,
-	.write_aai = default_spi_write_aai,
+	.max_data_read	= 8,
+	.max_data_write	= 5,
+	.check_trans	= sb600_spi_check_trans,
+	.command	= sb600_spi_send_command,
+	.multicommand	= default_spi_send_multicommand,
+	.read		= default_spi_read,
+	.write_256	= default_spi_write_256,
+	.write_aai	= default_spi_write_aai,
 };
 
 int sb600_probe_spi(struct pci_dev *dev)
diff --git a/serprog.c b/serprog.c
index b179ea4..4b0dc19 100644
--- a/serprog.c
+++ b/serprog.c
@@ -234,31 +234,31 @@  err_out:
 	return 1;
 }
 
-static int sp_check_commandavail(uint8_t command)
+static int sp_check_commandavail(uint8_t cmd)
 {
 	int byteoffs, bitoffs;
-	byteoffs = command / 8;
-	bitoffs = command % 8;
+	byteoffs = cmd / 8;
+	bitoffs = cmd % 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)) {
-		msg_pdbg("Warning: Automatic command availability check failed "
-			 "for cmd 0x%x - won't execute cmd\n", cmd);
+	if ((sp_check_avail_automatic) && (sp_check_commandavail(cmd) == 0))
 		return 1;
-		}
 	return 0;
 }
 
-static int sp_docommand(uint8_t command, uint32_t parmlen,
+static int sp_docommand(uint8_t cmd, uint32_t parmlen,
 			uint8_t *params, uint32_t retlen, void *retparms)
 {
 	unsigned char c;
-	if (sp_automatic_cmdcheck(command))
+	if (sp_automatic_cmdcheck(cmd)) {
+		msg_pdbg("Warning: Automatic command availability check failed "
+			 "for cmd 0x%x - won't execute cmd\n", cmd);
 		return 1;
-	if (write(sp_fd, &command, 1) != 1) {
+	}
+	if (write(sp_fd, &cmd, 1) != 1) {
 		msg_perr("Error: cannot write op code: %s\n", strerror(errno));
 		return 1;
 	}
@@ -316,8 +316,11 @@  static void sp_flush_stream(void)
 static int sp_stream_buffer_op(uint8_t cmd, uint32_t parmlen, uint8_t * parms)
 {
 	uint8_t *sp;
-	if (sp_automatic_cmdcheck(cmd))
+	if (sp_automatic_cmdcheck(cmd)) {
+		msg_pdbg("Warning: Automatic command availability check failed "
+			 "for cmd 0x%x - won't execute cmd\n", cmd);
 		return 1;
+	}
 	sp = malloc(1 + parmlen);
 	if (!sp) sp_die("Error: cannot malloc command buffer");
 	sp[0] = cmd;
@@ -332,6 +335,12 @@  static int sp_stream_buffer_op(uint8_t cmd, uint32_t parmlen, uint8_t * parms)
 	return 0;
 }
 
+static int serprog_spi_check_trans(struct flashctx *flash, unsigned int writecnt, unsigned int readcnt,
+				   const unsigned char *writearr)
+{
+		return sp_automatic_cmdcheck(S_CMD_O_SPIOP);
+}
+
 static int serprog_spi_send_command(struct flashctx *flash,
 				    unsigned int writecnt, unsigned int readcnt,
 				    const unsigned char *writearr,
@@ -342,6 +351,7 @@  static struct spi_programmer spi_programmer_serprog = {
 	.type		= SPI_CONTROLLER_SERPROG,
 	.max_data_read	= MAX_DATA_READ_UNLIMITED,
 	.max_data_write	= MAX_DATA_WRITE_UNLIMITED,
+	.check_trans	= serprog_spi_check_trans,
 	.command	= serprog_spi_send_command,
 	.multicommand	= default_spi_send_multicommand,
 	.read		= serprog_spi_read,
diff --git a/spi.c b/spi.c
index 94a76a7..046d095 100644
--- a/spi.c
+++ b/spi.c
@@ -30,12 +30,17 @@ 
 #include "programmer.h"
 #include "spi.h"
 
+int default_spi_check_trans(struct flashctx *flash, unsigned int writecnt, unsigned int readcnt,
+	   const unsigned char *writearr)
+{
+	return 0;
+}
+
 int spi_send_command(struct flashctx *flash, unsigned int writecnt,
 		     unsigned int readcnt, const unsigned char *writearr,
 		     unsigned char *readarr)
 {
-	return flash->pgm->spi.command(flash, writecnt, readcnt, writearr,
-				       readarr);
+	return flash->pgm->spi.command(flash, writecnt, readcnt, writearr, readarr);
 }
 
 int spi_send_multicommand(struct flashctx *flash, struct spi_command *cmds)
diff --git a/wbsio_spi.c b/wbsio_spi.c
index 778b983..ec14c28 100644
--- a/wbsio_spi.c
+++ b/wbsio_spi.c
@@ -108,6 +108,15 @@  static uint8_t determine_mode(struct flashctx *flash, unsigned int writecnt, uns
 	return 0;
 }
 
+static int wbsio_spi_check_trans(struct flashctx *flash, unsigned int writecnt, unsigned int readcnt,
+				 const unsigned char *writearr)
+{
+	if (determine_mode(flash, writecnt, readcnt, writearr) == 0)
+		return SPI_INVALID_LENGTH;
+
+	return 0;
+}
+
 static int wbsio_spi_send_command(struct flashctx *flash, unsigned int writecnt,
 				  unsigned int readcnt,
 				  const unsigned char *writearr,
@@ -119,6 +128,7 @@  static const struct spi_programmer spi_programmer_wbsio = {
 	.type = SPI_CONTROLLER_WBSIO,
 	.max_data_read = MAX_DATA_UNSPECIFIED,
 	.max_data_write = MAX_DATA_UNSPECIFIED,
+	.check_trans	= wbsio_spi_check_trans,
 	.command = wbsio_spi_send_command,
 	.multicommand = default_spi_send_multicommand,
 	.read = wbsio_spi_read,