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(a)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(a)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