[flashrom] [PATCH] dediprog: Use asynchronous bulk transfers for reading

Nico Huber nico.huber at secunet.com
Fri Feb 22 12:15:10 CET 2013


This utilizes libusb-1.0's interface for asynchronous transfers. A read
is split into 512 byte chunks as required by the dediprog hardware. Up
to eight chunks are scheduled for asynchronous transfer which results in
a viable speed-up. First tests have shown a three times speed increase.

Signed-off-by: Nico Huber <nico.huber at secunet.com>
---
 dediprog.c |   92 ++++++++++++++++++++++++++++++++++++++++++++++++++++++------
 1 file changed, 83 insertions(+), 9 deletions(-)

diff --git a/dediprog.c b/dediprog.c
index 93c4072..3cd7b01 100644
--- a/dediprog.c
+++ b/dediprog.c
@@ -31,6 +31,7 @@
 
 #define FIRMWARE_VERSION(x,y,z) ((x << 16) | (y << 8) | z)
 #define DEFAULT_TIMEOUT 3000
+#define DEDIPROG_ASYNC_TRANSFERS 8 /* at most 8 asynchronous transfers */
 static libusb_context *usb_ctx;
 static libusb_device_handle *dediprog_handle;
 static int dediprog_firmwareversion;
@@ -235,6 +236,38 @@ static int dediprog_set_spi_speed(unsigned int spispeed_idx)
 	return 0;
 }
 
+struct dediprog_transfer_status {
+	int error; /* OK if 0, ERROR else */
+	unsigned int queued_idx;
+	unsigned int finished_idx;
+};
+
+static void dediprog_bulk_read_cb(struct libusb_transfer *const transfer)
+{
+	struct dediprog_transfer_status *const status = (struct dediprog_transfer_status *)transfer->user_data;
+	if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
+		status->error = 1;
+		msg_perr("SPI bulk read failed!\n");
+	}
+	++status->finished_idx;
+}
+
+static int dediprog_bulk_read_poll(const struct dediprog_transfer_status *const status, const int finish)
+{
+	if (status->finished_idx >= status->queued_idx)
+		return 0;
+
+	do {
+		struct timeval timeout = { 10, 0 };
+		const int ret = libusb_handle_events_timeout_completed(usb_ctx, &timeout, NULL);
+		if (ret < 0) {
+			msg_perr("Polling read events failed: %i %s!\n", ret, libusb_error_name(ret));
+			return 1;
+		}
+	} while (finish && (status->finished_idx < status->queued_idx));
+	return 0;
+}
+
 /* Bulk read interface, will read multiple 512 byte chunks aligned to 512 bytes.
  * @start	start address
  * @len		length
@@ -243,7 +276,7 @@ static int dediprog_set_spi_speed(unsigned int spispeed_idx)
 static int dediprog_spi_bulk_read(struct flashctx *flash, uint8_t *buf,
 				  unsigned int start, unsigned int len)
 {
-	int ret, transferred;
+	int ret, err = 1;
 	unsigned int i;
 	/* chunksize must be 512, other sizes will NOT work at all. */
 	const unsigned int chunksize = 0x200;
@@ -253,6 +286,10 @@ static int dediprog_spi_bulk_read(struct flashctx *flash, uint8_t *buf,
 		chunksize & 0xff, (chunksize >> 8) & 0xff
 	};
 
+	struct dediprog_transfer_status status = { 0, 0, 0 };
+	struct libusb_transfer *transfers[DEDIPROG_ASYNC_TRANSFERS] = { NULL, };
+	struct libusb_transfer *transfer;
+
 	if ((start % chunksize) || (len % chunksize)) {
 		msg_perr("%s: Unaligned start=%i, len=%i! Please report a bug "
 			 "at flashrom at flashrom.org\n", __func__, start, len);
@@ -273,17 +310,54 @@ static int dediprog_spi_bulk_read(struct flashctx *flash, uint8_t *buf,
 		return 1;
 	}
 
-	for (i = 0; i < count; i++) {
-		ret = libusb_bulk_transfer(dediprog_handle, 0x80 | dediprog_endpoint,
-					   buf + i * chunksize, chunksize, &transferred, DEFAULT_TIMEOUT);
-		if ((ret < 0) || (transferred != chunksize)) {
-			msg_perr("SPI bulk read %i failed, expected %i, got %i %s!\n",
-				 i, chunksize, ret, libusb_error_name(ret));
-			return 1;
+	/*
+	 * Ring buffer of bulk transfers.
+	 * Poll until at least one transfer is ready,
+	 *   schedule next transfers until buffer is full.
+	 */
+
+	/* Allocate bulk transfers. */
+	for (i = 0; i < min(DEDIPROG_ASYNC_TRANSFERS, count); ++i) {
+		transfers[i] = libusb_alloc_transfer(0);
+		if (!transfers[i]) {
+			msg_perr("Allocating libusb transfer %i failed: %s!\n", i, libusb_error_name(ret));
+			goto _err_free;
 		}
 	}
 
-	return 0;
+	/* Now transfer requested chunks using libusb's asynchronous interface. */
+	while (!status.error && (status.queued_idx < count)) {
+		while ((status.queued_idx - status.finished_idx) < DEDIPROG_ASYNC_TRANSFERS) {
+			transfer = transfers[status.queued_idx % DEDIPROG_ASYNC_TRANSFERS];
+			libusb_fill_bulk_transfer(transfer, dediprog_handle, 0x80 | dediprog_endpoint,
+					(unsigned char *)buf + status.queued_idx * chunksize, chunksize,
+					dediprog_bulk_read_cb, &status, DEFAULT_TIMEOUT);
+			transfer->flags |= LIBUSB_TRANSFER_SHORT_NOT_OK;
+			ret = libusb_submit_transfer(transfer);
+			if (ret < 0) {
+				msg_perr("Submitting SPI bulk read %i failed: %i %s!\n",
+					 status.queued_idx, ret, libusb_error_name(ret));
+				goto _err_free;
+			}
+			++status.queued_idx;
+		}
+		if (dediprog_bulk_read_poll(&status, 0))
+			goto _err_free;
+	}
+	/* Wait for transfers to finish. */
+	if (dediprog_bulk_read_poll(&status, 1))
+		goto _err_free;
+	/* Check if everything has been transmitted. */
+	if ((status.finished_idx < count) || status.error)
+		goto _err_free;
+
+	err = 0;
+
+_err_free:
+	dediprog_bulk_read_poll(&status, 1);
+	for (i = 0; i < DEDIPROG_ASYNC_TRANSFERS; ++i)
+		if (transfers[i]) libusb_free_transfer(transfers[i]);
+	return err;
 }
 
 static int dediprog_spi_read(struct flashctx *flash, uint8_t *buf,
-- 
1.7.9.5





More information about the flashrom mailing list