Patchwork [4/4] usbblaster: Use asynchronous bulk transfers

login
register
about
Submitter Kyösti Mälkki
Date 2013-03-30 18:31:35
Message ID <1364668295-4612-4-git-send-email-kyosti.malkki@gmail.com>
Download mbox | patch
Permalink /patch/3903/
State New
Headers show

Comments

Kyösti Mälkki - 2013-03-30 18:31:35
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

Patch

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,