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 <[email protected]>
---
 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 [email protected]\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


_______________________________________________
flashrom mailing list
[email protected]
http://www.flashrom.org/mailman/listinfo/flashrom

Reply via email to