There is minor speedup for using this.
Signed-off-by: Kyösti Mälkki kyosti.malkki@gmail.com --- usbblaster.h | 63 +++++++++++++ usbblaster_spi.c | 282 ++++++++++++++++++++++++++++++++++++++++++------------- 2 files changed, 278 insertions(+), 67 deletions(-) create mode 100644 usbblaster.h
diff --git a/usbblaster.h b/usbblaster.h new file mode 100644 index 0000000..d79384b --- /dev/null +++ b/usbblaster.h @@ -0,0 +1,63 @@ +/* + * This file is part of the flashrom project. + * + * Copyright (C) 2013 Kyösti Mälkki kyosti.malkki@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; version 2 of the License. + * + * 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 + */ + +/* Please keep sorted by vendor ID, then device ID. */ +#define ALTERA_VID 0x09fb +#define ALTERA_USBBLASTER_PID 0x6001 + +// command bytes +#define BIT_BYTE (1<<7) // byte mode (rather than bitbang) +#define BIT_READ (1<<6) // read request +#define BIT_LED (1<<5) +#define BIT_CS (1<<3) +#define BIT_TMS (1<<1) +#define BIT_CLK (1<<0) + +#define PAYLOAD_TIMEOUT 0 +#define URB_COUNT 12 +#define MAX_PAYLOAD 64 + +enum spi_direction { + READ_TDO, WRITE_TDI +}; + +struct blaster_block { + struct libusb_device_handle *usb_dev; + struct libusb_context *context; + uint8_t endpoint_in; + uint8_t endpoint_out; + + enum spi_direction dir; + uint8_t *buf; + size_t len; + uint32_t wr_queued; + uint32_t wr_done; + uint32_t rd_queued; + uint32_t rd_done; + uint32_t complete; +}; + +struct blaster_control { + uint8_t buf[MAX_PAYLOAD]; + size_t len; + + struct blaster_block *block; + struct libusb_transfer *transfer; +}; + diff --git a/usbblaster_spi.c b/usbblaster_spi.c index 86fd573..5b5e53a 100644 --- a/usbblaster_spi.c +++ b/usbblaster_spi.c @@ -2,6 +2,7 @@ * This file is part of the flashrom project. * * Copyright (C) 2012 James Laird jhl@mafipulation.org + * Copyright (C) 2013 Kyösti Mälkki kyosti.malkki@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 @@ -39,15 +40,15 @@ #include <stdio.h> #include <string.h> #include <stdlib.h> +#include <unistd.h> #include <ctype.h> #include <ftdi.h> +#include <libusb.h> #include "flash.h" #include "programmer.h" #include "spi.h" +#include "usbblaster.h"
-/* Please keep sorted by vendor ID, then device ID. */ -#define ALTERA_VID 0x09fb -#define ALTERA_USBBLASTER_PID 0x6001
const struct dev_entry devs_usbblasterspi[] = { {ALTERA_VID, ALTERA_USBBLASTER_PID, OK, "Altera", "USB-Blaster"}, @@ -59,28 +60,14 @@ static const struct spi_programmer spi_programmer_usbblaster;
static struct ftdi_context ftdic;
-// command bytes -#define BIT_BYTE (1<<7) // byte mode (rather than bitbang) -#define BIT_READ (1<<6) // read request -#define BIT_LED (1<<5) -#define BIT_CS (1<<3) -#define BIT_TMS (1<<1) -#define BIT_CLK (1<<0) - -#define BUF_SIZE 64 - -/* The programmer shifts bits in the wrong order for SPI, so we use this method to reverse the bits when needed. - * http://graphics.stanford.edu/~seander/bithacks.html#ReverseByteWith32Bits */ -uint8_t reverse(uint8_t b) -{ - return ((b * 0x0802LU & 0x22110LU) | (b * 0x8020LU & 0x88440LU)) * 0x10101LU >> 16; -} +static struct blaster_block block_template; +static struct blaster_control tcs[URB_COUNT];
/* Returns 0 upon success, a negative number upon errors. */ int usbblaster_spi_init(void) { - uint8_t buf[BUF_SIZE + 1]; + uint8_t buf[MAX_PAYLOAD + 1];
if (ftdi_init(&ftdic) < 0) return -1; @@ -101,7 +88,7 @@ int usbblaster_spi_init(void) }
if (ftdi_write_data_set_chunksize(&ftdic, 4096) < 0 || - ftdi_read_data_set_chunksize(&ftdic, BUF_SIZE) < 0) { + ftdi_read_data_set_chunksize(&ftdic, MAX_PAYLOAD) < 0) { msg_perr("USB-Blaster set chunk size failed\n"); return -1; } @@ -117,69 +104,230 @@ int usbblaster_spi_init(void) return -1; }
+ block_template.usb_dev = ftdic.usb_dev; + block_template.context = ftdic.usb_ctx; + /* Note the reversed view on endpoint direction. */ + block_template.endpoint_in = ftdic.out_ep; + block_template.endpoint_out = ftdic.in_ep; + register_spi_programmer(&spi_programmer_usbblaster); return 0; }
-static int send_write(unsigned int writecnt, const unsigned char *writearr) +static void init_block(struct blaster_block *tb, enum spi_direction dir, uint8_t *buf, size_t len) +{ + memcpy(tb, &block_template, sizeof(struct blaster_block)); + tb->buf = buf; + tb->len = len; + tb->dir = dir; + /* On writes fast-forward read pointer to end. */ + if (tb->dir == WRITE_TDI) + tb->rd_done = tb->len; +} + +static int alloc_transfer(struct blaster_block *tb) { int i; - uint8_t buf[BUF_SIZE]; + memset(tcs, 0, sizeof(tcs)); + for (i = 0; i < URB_COUNT; i++) { + tcs[i].block = tb; + tcs[i].transfer = libusb_alloc_transfer(0); + if (!tcs[i].transfer) + return -1; + } + return 0; +} + +static void free_transfer(struct blaster_block *tb) +{ + int i; + for (i = 0; i < URB_COUNT; i++) { + libusb_free_transfer(tcs[i].transfer); + } +} + + +/* + * + * + */ + +static int blaster_write(struct blaster_control *tc); +static int blaster_read(struct blaster_control *tc, int forced); + +/* The programmer shifts bits in the wrong order for SPI, so we use this method to reverse the bits when needed. + * http://graphics.stanford.edu/~seander/bithacks.html#ReverseByteWith32Bits */ +static uint8_t reverse(uint8_t b) +{ + return ((b * 0x0802LU & 0x22110LU) | (b * 0x8020LU & 0x88440LU)) * 0x10101LU >> 16; +}
- memset(buf, 0, sizeof(buf)); - while (writecnt) { - unsigned int n_write = min(writecnt, BUF_SIZE - 1); - msg_pspew("writing %d-byte packet\n", n_write);
- buf[0] = BIT_BYTE | (uint8_t)n_write; +static void blaster_write_cb(struct libusb_transfer *transfer) +{ + struct blaster_control *tc = (struct blaster_control *) transfer->user_data; + struct blaster_block *tb = tc->block; + + transfer->user_data = NULL; + if (transfer->status != LIBUSB_TRANSFER_COMPLETED) + return; + + tb->wr_done += transfer->length; + if ((tb->wr_done >= tb->len) && (tb->rd_done >= tb->len)) + tb->complete = 1; + + blaster_write(tc); +} + +static int blaster_write(struct blaster_control *tc) +{ + struct blaster_block *tb = tc->block; + unsigned int n_write; + int i, ret; + + n_write = tb->len - tb->wr_queued; + if (!n_write) + return 0; + if (n_write > MAX_PAYLOAD - 1) + n_write = MAX_PAYLOAD - 1; + + if (tb->dir==WRITE_TDI) { + tc->buf[0] = BIT_BYTE | (uint8_t) n_write; for (i = 0; i < n_write; i++) { - buf[i+1] = reverse(writearr[i]); - } - if (ftdi_write_data(&ftdic, buf, n_write + 1) < 0) { - msg_perr("USB-Blaster write failed\n"); - return -1; + tc->buf[i + 1] = reverse(tb->buf[tb->wr_queued + i]); } - - writearr += n_write; - writecnt -= n_write; + tc->len = n_write + 1; + } else { + memset(tc->buf, 0, MAX_PAYLOAD); + tc->buf[0] = BIT_BYTE | BIT_READ | (uint8_t) n_write; + tc->len = n_write + 1; } - return 0; + + memset(tc->transfer, 0, sizeof(struct libusb_transfer)); + libusb_fill_bulk_transfer(tc->transfer, tb->usb_dev, tb->endpoint_out, + tc->buf, tc->len, blaster_write_cb, tc, PAYLOAD_TIMEOUT); + + do { + ret = libusb_submit_transfer(tc->transfer); + } while (ret < 0); + + tb->wr_queued += n_write; + return ret; }
-static int send_read(unsigned int readcnt, unsigned char *readarr) + +static void blaster_read_cb(struct libusb_transfer *transfer) { + struct blaster_control *tc = (struct blaster_control *) transfer->user_data; + struct blaster_block *tb = tc->block; int i; - unsigned int n_read; - uint8_t buf[BUF_SIZE]; - memset(buf, 0, sizeof(buf));
- n_read = readcnt; - while (n_read) { - unsigned int payload_size = min(n_read, BUF_SIZE - 1); - msg_pspew("reading %d-byte packet\n", payload_size); + transfer->user_data = NULL; + if (transfer->status != LIBUSB_TRANSFER_COMPLETED) + return;
- buf[0] = BIT_BYTE | BIT_READ | (uint8_t)payload_size; - if (ftdi_write_data(&ftdic, buf, payload_size + 1) < 0) { - msg_perr("USB-Blaster write failed\n"); - return -1; - } - n_read -= payload_size; - }; - - n_read = readcnt; - while (n_read) { - int ret = ftdi_read_data(&ftdic, readarr, n_read); - if (ret < 0) { - msg_perr("USB-Blaster read failed\n"); - return -1; - } - for (i = 0; i < ret; i++) { - readarr[i] = reverse(readarr[i]); + /* Endpoint IN payload always begins with two status bytes. */ + if (transfer->actual_length > 2) { + for (i = 0; i < (transfer->actual_length - 2); i++) { + tb->buf[tb->rd_done + i] = reverse(transfer->buffer[i + 2]); } - n_read -= ret; - readarr += ret; + tb->rd_done += i; } - return 0; + + if ((tb->rd_done >= tb->len) && (tb->wr_done >= tb->len)) + tb->complete = 1; + + blaster_read(tc, (tb->rd_done < tb->len)); +} + +static int blaster_read(struct blaster_control *tc, int forced) +{ + struct blaster_block *tb = tc->block; + int ret; + + if (!forced && (tb->rd_queued >= tb->len)) + return 0; + + memset(tc->buf, 0, sizeof(tc->buf)); + tc->len = MAX_PAYLOAD; + + memset(tc->transfer, 0, sizeof(struct libusb_transfer)); + libusb_fill_bulk_transfer(tc->transfer, tb->usb_dev, tb->endpoint_in, + tc->buf, tc->len, blaster_read_cb, tc, PAYLOAD_TIMEOUT); + + do { + ret = libusb_submit_transfer(tc->transfer); + } while (ret < 0); + + /* Endpoint IN payload always begins with two status bytes. */ + tb->rd_queued += MAX_PAYLOAD - 2; + return ret; +} + +static int do_transfer(struct blaster_block *tb) +{ + struct timeval timeout; + int i, j, ret = -1; + + if (tb->dir==READ_TDO) { + /* To read data, must write data. */ + for (i = 0; i < URB_COUNT / 2 - 2; i++) + blaster_write(&tcs[i]); + for (; i < URB_COUNT; i++) + blaster_read(&tcs[i], 0); + } else { + /* No need to read to write. */ + for (i = 0; i < URB_COUNT; i++) + blaster_write(&tcs[i]); + } + + timeout.tv_sec = 0; + timeout.tv_usec = 5000; + while (!tb->complete) { + ret = libusb_handle_events_timeout(tb->context, &timeout); +// if (ret<0) +// break; + } + + /* Cleanup URBs. */ + for (i = 0; i < URB_COUNT; i++) { + libusb_cancel_transfer(tcs[i].transfer); + } + + timeout.tv_sec = 0; + timeout.tv_usec = 5000; + do { + libusb_handle_events_timeout(tb->context, &timeout); + j = 0; + for (i = 0; i < URB_COUNT; i++) { + if (tcs[i].transfer->user_data) + j++; + } + } while (j); + + return ret; +} + +static int send_write(unsigned int writecnt, const unsigned char *writearr) +{ + struct blaster_block block; + int ret; + init_block(&block, WRITE_TDI, (unsigned char*) writearr, writecnt); + alloc_transfer(&block); + ret = do_transfer(&block); + free_transfer(&block); + return ret; +} + +static int send_read(unsigned int readcnt, unsigned char *readarr) +{ + struct blaster_block block; + int ret; + init_block(&block, READ_TDO, readarr, readcnt); + alloc_transfer(&block); + ret = do_transfer(&block); + free_transfer(&block); + return ret; }
/* Returns 0 upon success, a negative number upon errors. */ @@ -213,8 +361,8 @@ static int usbblaster_spi_send_command(struct flashctx *flash, unsigned int writ
static const struct spi_programmer spi_programmer_usbblaster = { .type = SPI_CONTROLLER_USBBLASTER, - .max_data_read = 256, - .max_data_write = 256, + .max_data_read = 8192, + .max_data_write = 8192, .command = usbblaster_spi_send_command, .multicommand = default_spi_send_multicommand, .read = default_spi_read,