New version, only cosmetic changes compared to last version.
The Bus Pirate firmware (at least v6.1 and earlier) can't handle UART input buffer overflow in BBIO mode, and sending a sequence of 0x00 too fast apparently triggers such an UART input buffer overflow. Wait 10 ms after sending each 0x00 byte during init to give the Bus Pirate enough time to handle the input. This fixes a Bus Pirate hang if the previous flashrom run was aborted by the user.
The Bus Pirate firmware v6.1 and earlier use the wrong (too slow) SPI speed if more than 2 MHz are requested. Automatically downgrade SPI speed to 2 MHz for affected firmware versions.
Detect Bus Pirate hardware and firmware versions to allow quirk handling.
The Bus Pirate init sequence has lots of open-coded sequences which wait for a given string on the serial line. Refactor them into buspirate_wait_for_string().
User-visible changes: Fix hang, use 2 MHz SPI speed on buggy firmware. Note: This patch does _not_ include support for the fast access mode present in firmware 5.5 and later. Tested with Bus Pirate hardware v3a and v3b, firmwares 4.0, 5.2 and 6.2-beta.
Signed-off-by: Carl-Daniel Hailfinger c-d.hailfinger.devel.2006@gmx.net
Index: flashrom-buspirate_initcleanup/buspirate_spi.c =================================================================== --- flashrom-buspirate_initcleanup/buspirate_spi.c (Revision 1546) +++ flashrom-buspirate_initcleanup/buspirate_spi.c (Arbeitskopie) @@ -83,7 +83,8 @@ msg_perr("Zero length command!\n"); return 1; } - msg_pspew("Sending"); + if (writecnt) + msg_pspew("Sending"); for (i = 0; i < writecnt; i++) msg_pspew(" 0x%02x", buf[i]); #ifdef FAKE_COMMUNICATION @@ -103,19 +104,32 @@ if (ret) return ret; #endif - msg_pspew(", receiving"); + if (readcnt) + msg_pspew(", receiving"); for (i = 0; i < readcnt; i++) msg_pspew(" 0x%02x", buf[i]); msg_pspew("\n"); return 0; }
-static int buspirate_spi_send_command(struct flashctx *flash, - unsigned int writecnt, - unsigned int readcnt, - const unsigned char *writearr, - unsigned char *readarr); +static int buspirate_wait_for_string(unsigned char *buf, char *key) +{ + unsigned int keylen = strlen(key); + int ret;
+ ret = buspirate_sendrecv(buf, 0, keylen); + while (!ret) { + if (!memcmp(buf, key, keylen)) + return 0; + memmove(buf, buf + 1, keylen - 1); + ret = buspirate_sendrecv(buf + keylen - 1, 0, 1); + } + return ret; +} + +static int buspirate_spi_send_command(struct flashctx *flash, unsigned int writecnt, unsigned int readcnt, + const unsigned char *writearr, unsigned char *readarr); + static const struct spi_programmer spi_programmer_buspirate = { .type = SPI_CONTROLLER_BUSPIRATE, .max_data_read = 12, @@ -146,17 +160,15 @@
/* Exit raw SPI mode (enter raw bitbang mode) */ bp_commbuf[0] = 0x00; - ret = buspirate_sendrecv(bp_commbuf, 1, 5); - if (ret) + if ((ret = buspirate_sendrecv(bp_commbuf, 1, 0))) goto out_shutdown; - if (memcmp(bp_commbuf, "BBIO", 4)) { - msg_perr("Entering raw bitbang mode failed!\n"); - ret = 1; + if ((ret = buspirate_wait_for_string(bp_commbuf, "BBIO"))) goto out_shutdown; - } - msg_pdbg("Raw bitbang mode version %c\n", bp_commbuf[4]); - if (bp_commbuf[4] != '1') { - msg_perr("Can't handle raw bitbang mode version %c!\n", bp_commbuf[4]); + if ((ret = buspirate_sendrecv(bp_commbuf, 0, 1))) + goto out_shutdown; + msg_pdbg("Raw bitbang mode version %c\n", bp_commbuf[0]); + if (bp_commbuf[0] != '1') { + msg_perr("Can't handle raw bitbang mode version %c!\n", bp_commbuf[0]); ret = 1; goto out_shutdown; } @@ -181,18 +193,26 @@ return ret; }
+#define BP_FWVERSION(a,b) ((a) << 8 | (b)) + int buspirate_spi_init(void) { char *dev = NULL; char *speed = NULL; + char *tmp; + unsigned int fw_version_major = 0; + unsigned int fw_version_minor = 0; int spispeed = 0x7; int ret = 0; int i;
dev = extract_programmer_param("dev"); - if (!dev || !strlen(dev)) { - msg_perr("No serial device given. Use flashrom -p " - "buspirate_spi:dev=/dev/ttyUSB0\n"); + if (dev && !strlen(dev)) { + free(dev); + dev = NULL; + } + if (!dev) { + msg_perr("No serial device given. Use flashrom -p buspirate_spi:dev=/dev/ttyUSB0\n"); return 1; }
@@ -209,9 +229,6 @@ } free(speed);
- /* This works because speeds numbering starts at 0 and is contiguous. */ - msg_pdbg("SPI speed is %sHz\n", spispeeds[spispeed].name); - /* Default buffer size is 19: 16 bytes data, 3 bytes control. */ #define DEFAULT_BUFSIZE (16 + 3) bp_commbuf = malloc(DEFAULT_BUFSIZE); @@ -235,66 +252,118 @@ return 1;
/* This is the brute force version, but it should work. */ - for (i = 0; i < 19; i++) { + for (i = 0; i < 20; i++) { /* Enter raw bitbang mode */ bp_commbuf[0] = 0x00; /* Send the command, don't read the response. */ ret = buspirate_sendrecv(bp_commbuf, 1, 0); if (ret) return ret; - /* Read any response and discard it. */ - sp_flush_incoming(); + /* The old way to handle responses from a Bus Pirate already in BBIO mode was to flush any + * response which came in over serial. Unfortunately that does not work reliably on Linux + * with FTDI USB-serial. + */ + //sp_flush_incoming(); + /* The Bus Pirate can't handle UART input buffer overflow in BBIO mode, and sending a sequence + * of 0x00 too fast apparently triggers such an UART input buffer overflow. + */ + usleep(10000); } - /* USB is slow. The Bus Pirate is even slower. Apparently the flush - * action above is too fast or too early. Some stuff still remains in - * the pipe after the flush above, and one additional flush is not - * sufficient either. Use a 1.5 ms delay inside the loop to make - * mostly sure that at least one USB frame had time to arrive. - * Looping only 5 times is not sufficient and causes the - * occasional failure. - * Folding the delay into the loop above is not reliable either. - */ - for (i = 0; i < 10; i++) { - usleep(1500); - /* Read any response and discard it. */ - sp_flush_incoming(); + /* We know that 20 commands of \0 should elicit at least one BBIO1 response. */ + if ((ret = buspirate_wait_for_string(bp_commbuf, "BBIO"))) + return ret; + + /* Reset the Bus Pirate. */ + bp_commbuf[0] = 0x0f; + /* Send the command, don't read the response. */ + if ((ret = buspirate_sendrecv(bp_commbuf, 1, 0))) + return ret; + if ((ret = buspirate_wait_for_string(bp_commbuf, "irate "))) + return ret; + /* Read the hardware version string. Last byte of the buffer is reserved for \0. */ + for (i = 0; i < DEFAULT_BUFSIZE - 1; i++) { + if ((ret = buspirate_sendrecv(bp_commbuf + i, 0, 1))) + return ret; + if (strchr("\r\n\t ", bp_commbuf[i])) + break; } + bp_commbuf[i] = '\0'; + msg_pdbg("Detected Bus Pirate hardware %s\n", bp_commbuf); + + if ((ret = buspirate_wait_for_string(bp_commbuf, "irmware "))) + return ret; + /* Read the firmware version string. Last byte of the buffer is reserved for \0. */ + for (i = 0; i < DEFAULT_BUFSIZE - 1; i++) { + if ((ret = buspirate_sendrecv(bp_commbuf + i, 0, 1))) + return ret; + if (strchr("\r\n\t ", bp_commbuf[i])) + break; + } + bp_commbuf[i] = '\0'; + msg_pdbg("Detected Bus Pirate firmware "); + if (bp_commbuf[0] != 'v') + msg_pdbg("(unknown version number format)"); + else if (!strchr("0123456789", bp_commbuf[1])) + msg_pdbg("(unknown version number format)"); + else { + fw_version_major = strtoul((char *)bp_commbuf + 1, &tmp, 10); + while ((*tmp != '\0') && !strchr("0123456789", *tmp)) + tmp++; + fw_version_minor = strtoul(tmp, NULL, 10); + msg_pdbg("%u.%u", fw_version_major, fw_version_minor); + } + msg_pdbg2(" ("%s")", bp_commbuf); + msg_pdbg("\n"); + + if ((ret = buspirate_wait_for_string(bp_commbuf, "HiZ>"))) + return ret; + + /* Tell the user about missing SPI binary mode in firmware 2.3 and older. */ + if (BP_FWVERSION(fw_version_major, fw_version_minor) < BP_FWVERSION(2, 4)) { + msg_pinfo("Bus Pirate firmware 2.3 and older does not support binary SPI access.\n"); + msg_pinfo("Please upgrade to the latest firmware (at least 2.4).\n"); + return SPI_PROGRAMMER_ERROR; + } + + /* Workaround for broken speed settings in firmware 6.1 and older. */ + if (BP_FWVERSION(fw_version_major, fw_version_minor) < BP_FWVERSION(6, 2)) + if (spispeed > 0x4) { + msg_perr("Bus Pirate firmware 6.1 and older does not support SPI speeds above 2 MHz. " + "Limiting speed to 2 MHz.\n"); + msg_pinfo("It is recommended to upgrade to firmware 6.2 or newer.\n"); + spispeed = 0x4; + } + + /* This works because speeds numbering starts at 0 and is contiguous. */ + msg_pdbg("SPI speed is %sHz\n", spispeeds[spispeed].name); + /* Enter raw bitbang mode */ - bp_commbuf[0] = 0x00; - ret = buspirate_sendrecv(bp_commbuf, 1, 5); - if (ret) + for (i = 0; i < 20; i++) { + bp_commbuf[0] = 0x00; + if ((ret = buspirate_sendrecv(bp_commbuf, 1, 0))) + return ret; + } + if ((ret = buspirate_wait_for_string(bp_commbuf, "BBIO"))) return ret; - if (memcmp(bp_commbuf, "BBIO", 4)) { - msg_perr("Entering raw bitbang mode failed!\n"); - msg_pdbg("Got %02x%02x%02x%02x%02x\n", - bp_commbuf[0], bp_commbuf[1], bp_commbuf[2], - bp_commbuf[3], bp_commbuf[4]); + if ((ret = buspirate_sendrecv(bp_commbuf, 0, 1))) + return ret; + msg_pdbg("Raw bitbang mode version %c\n", bp_commbuf[0]); + if (bp_commbuf[0] != '1') { + msg_perr("Can't handle raw bitbang mode version %c!\n", bp_commbuf[0]); return 1; } - msg_pdbg("Raw bitbang mode version %c\n", bp_commbuf[4]); - if (bp_commbuf[4] != '1') { - msg_perr("Can't handle raw bitbang mode version %c!\n", - bp_commbuf[4]); - return 1; - } /* Enter raw SPI mode */ bp_commbuf[0] = 0x01; - ret = buspirate_sendrecv(bp_commbuf, 1, 4); - if (ret) + ret = buspirate_sendrecv(bp_commbuf, 1, 0); + if ((ret = buspirate_wait_for_string(bp_commbuf, "SPI"))) return ret; - if (memcmp(bp_commbuf, "SPI", 3)) { - msg_perr("Entering raw SPI mode failed!\n"); - msg_pdbg("Got %02x%02x%02x%02x\n", - bp_commbuf[0], bp_commbuf[1], bp_commbuf[2], - bp_commbuf[3]); + if ((ret = buspirate_sendrecv(bp_commbuf, 0, 1))) + return ret; + msg_pdbg("Raw SPI mode version %c\n", bp_commbuf[0]); + if (bp_commbuf[0] != '1') { + msg_perr("Can't handle raw SPI mode version %c!\n", bp_commbuf[0]); return 1; } - msg_pdbg("Raw SPI mode version %c\n", bp_commbuf[3]); - if (bp_commbuf[3] != '1') { - msg_perr("Can't handle raw SPI mode version %c!\n", - bp_commbuf[3]); - return 1; - }
/* Initial setup (SPI peripherals config): Enable power, CS high, AUX */ bp_commbuf[0] = 0x40 | 0xb;