spi: Prepare for multi i/o and dummy bytes

Multi-i/o commands split SPI transactions into multiple phases that
can be transferred over 1, 2 or 4 wires. For this, we adapt `struct
spi_command` with a new enum, specifying the transfer mode, and ad-
ditional size fields.  While we are at it, move everything related
into a new header file `spi_command.h` so we won't further clutter
`flash.h`.

On the master side, we add respective feature flags for the multi-
i/o modes.

See also the comment in `spi_command.h` about multi-i/o commands.

Change-Id: I79debb845f1c8fec77e0556853ffb01735e73ab8
Signed-off-by: Nico Huber <nico.h@gmx.de>
Reviewed-on: https://review.sourcearcade.org/c/flashprog/+/44
Reviewed-by: Arthur Heymans <arthur@aheymans.xyz>
diff --git a/ft2232_spi.c b/ft2232_spi.c
index 889db98..a82153d 100644
--- a/ft2232_spi.c
+++ b/ft2232_spi.c
@@ -22,6 +22,7 @@
 #include <ctype.h>
 #include "flash.h"
 #include "programmer.h"
+#include "spi_command.h"
 #include "spi.h"
 #include <ftdi.h>
 
@@ -206,9 +207,9 @@
 		/* commands for CS# assertion and de-assertion: */
 		cmd_len + cmd_len
 		/* commands for either a write, a read or both: */
-		+ (cmd->writecnt && cmd->readcnt ? cmd_len + cmd_len : cmd_len)
+		+ (spi_write_len(cmd) && spi_read_len(cmd) ? cmd_len + cmd_len : cmd_len)
 		/* payload (only writecnt; readcnt concerns another buffer): */
-		+ cmd->writecnt
+		+ spi_write_len(cmd)
 		<= buffer_size;
 }
 
@@ -224,9 +225,11 @@
 	/*
 	 * Minimize FTDI-calls by packing as many commands as possible together.
 	 */
-	for (; cmds->writecnt || cmds->readcnt; cmds++) {
+	for (; !spi_is_empty(cmds); cmds++) {
+		const size_t writecnt = spi_write_len(cmds);
+		const size_t readcnt = spi_read_len(cmds);
 
-		if (cmds->writecnt > 65536 || cmds->readcnt > 65536)
+		if (writecnt > 65536 || readcnt > 65536)
 			return SPI_INVALID_LENGTH;
 
 		if (!ft2232_spi_command_fits(cmds, FTDI_HW_BUFFER_SIZE - i)) {
@@ -241,19 +244,19 @@
 		buf[i++] = spi_data->pindir;
 
 		/* WREN, OP(PROGRAM, ERASE), ADDR, DATA */
-		if (cmds->writecnt) {
+		if (writecnt) {
 			buf[i++] = MPSSE_DO_WRITE | MPSSE_WRITE_NEG;
-			buf[i++] = (cmds->writecnt - 1) & 0xff;
-			buf[i++] = ((cmds->writecnt - 1) >> 8) & 0xff;
-			memcpy(buf + i, cmds->writearr, cmds->writecnt);
-			i += cmds->writecnt;
+			buf[i++] = (writecnt - 1) & 0xff;
+			buf[i++] = ((writecnt - 1) >> 8) & 0xff;
+			memcpy(buf + i, cmds->writearr, writecnt);
+			i += writecnt;
 		}
 
 		/* An optional read command */
-		if (cmds->readcnt) {
+		if (readcnt) {
 			buf[i++] = MPSSE_DO_READ;
-			buf[i++] = (cmds->readcnt - 1) & 0xff;
-			buf[i++] = ((cmds->readcnt - 1) >> 8) & 0xff;
+			buf[i++] = (readcnt - 1) & 0xff;
+			buf[i++] = ((readcnt - 1) >> 8) & 0xff;
 		}
 
 		/* Add final de-assert CS# */
@@ -263,8 +266,7 @@
 		buf[i++] = spi_data->pindir;
 
 		/* continue if there is no read-cmd and further cmds exist */
-		if (!cmds->readcnt &&
-				((cmds + 1)->writecnt || (cmds + 1)->readcnt) &&
+		if (!readcnt && !spi_is_empty(cmds + 1) &&
 				ft2232_spi_command_fits((cmds + 1), FTDI_HW_BUFFER_SIZE - i)) {
 			continue;
 		}
@@ -276,8 +278,8 @@
 			break;
 		}
 
-		if (cmds->readcnt) {
-			ret = get_buf(ftdic, cmds->readarr, cmds->readcnt);
+		if (readcnt) {
+			ret = get_buf(ftdic, cmds->readarr, readcnt);
 			if (ret) {
 				msg_perr("get_buf failed: %i\n", ret);
 				break;