Author: hailfinger Date: Fri Aug 17 19:30:43 2012 New Revision: 1576 URL: http://flashrom.org/trac/flashrom/changeset/1576
Log: Bus Pirate init cleanup and fixes.
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().
Signed-off-by: Carl-Daniel Hailfinger c-d.hailfinger.devel.2006@gmx.net Acked-by: Carl-Daniel Hailfinger c-d.hailfinger.devel.2006@gmx.net
Modified: trunk/buspirate_spi.c
Modified: trunk/buspirate_spi.c ============================================================================== --- trunk/buspirate_spi.c Thu Aug 16 01:06:32 2012 (r1575) +++ trunk/buspirate_spi.c Fri Aug 17 19:30:43 2012 (r1576) @@ -84,7 +84,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 @@ -104,18 +105,31 @@ 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, @@ -147,17 +161,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; } @@ -182,18 +194,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; }
@@ -210,9 +230,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); @@ -236,64 +253,116 @@ 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); + } + /* 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; } - /* 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(); + 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; } - /* Enter raw bitbang mode */ - bp_commbuf[0] = 0x00; - ret = buspirate_sendrecv(bp_commbuf, 1, 5); - if (ret) + 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; - 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]); - return 1; + + /* 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 */ + for (i = 0; i < 20; i++) { + bp_commbuf[0] = 0x00; + if ((ret = buspirate_sendrecv(bp_commbuf, 1, 0))) + return ret; } - 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_wait_for_string(bp_commbuf, "BBIO"))) + return ret; + 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; } /* 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]); - 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]); + 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; }