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@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@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,