New version: Fix error handling. libusb returns the number of bytes read/written, and those are nonzero in our case, triggering a false abort.
Support bulk read on Dediprog SF100. Should result in native speed for plain read and erase. Should result in a measurable speedup for writes due to a fast verify.
Read tests with chunksize = 0x20 would be appreciated. Read tests with chunksize = 0x1 would be appreciated. Both should be a lot slower, but still yield correct images.
Packet size is 512 bytes. Depending on your USB hardware and the Dediprog firmware version, this may not work at all. Add lots of error checking where it was missing before. Somewhat tested, experimental, may hang/crash the programmer.
Write tests of random images are appreciated.
Please note that flashrom needs full access permissions for the USB device.
Signed-off-by: Carl-Daniel Hailfinger c-d.hailfinger.devel.2006@gmx.net
Index: flashrom-dediprog_read_bulk/dediprog.c =================================================================== --- flashrom-dediprog_read_bulk/dediprog.c (Revision 1232) +++ flashrom-dediprog_read_bulk/dediprog.c (Arbeitskopie) @@ -27,6 +27,7 @@
#define DEFAULT_TIMEOUT 3000 static usb_dev_handle *dediprog_handle; +static int dediprog_endpoint;
#if 0 /* Might be useful for other pieces of code as well. */ @@ -150,9 +151,68 @@
int dediprog_spi_read(struct flashchip *flash, uint8_t *buf, int start, int len) { + int ret = 0; + int i; + int chunksize = 0x200; /* 512, other sizes may not work at all. */ + const int bulktransfer_chunksize = 0x200; /* 512, other sizes may not work at all. */ + /* count only covers full chunksize blocks. */ + int count = (len - start % chunksize) / chunksize; + /* Guessed. */ + const char count_and_chunk[] = { + count & 0xff, + (count >> 8) & 0xff, + chunksize & 0xff, + (chunksize >> 8) & 0xff}; + int residue = start % chunksize ? chunksize - start % chunksize : 0; + int bulkoffset = (start + residue) / chunksize; + char tmpbuf[bulktransfer_chunksize]; + msg_pspew("%s, start=0x%x, len=0x%x\n", __func__, start, len); - /* Chosen read length is 16 bytes for now. */ - return spi_read_chunked(flash, buf, start, len, 16); + if (residue) { + msg_pdbg("Slow read for partial block from 0x%x, length 0x%x\n", + start, residue); + ret = spi_read_chunked(flash, buf, start, residue, 16); + } + if (ret) + return ret; + + /* Command Read SPI Bulk. No idea which read command is used on the + * SPI side. offset is a total guess, I only saw it being 0 everywhere. + */ + ret = usb_control_msg(dediprog_handle, 0x42, 0x20, 0x0000, bulkoffset, (char *)count_and_chunk, sizeof(count_and_chunk), DEFAULT_TIMEOUT); + if (ret != sizeof(count_and_chunk)) { + msg_perr("Command Read SPI Bulk failed, %i %s!\n", ret, + usb_strerror()); + return 1; + } + /* ret was 4 if successful. Reset it. */ + ret = 0; + + for (i = 0; i < count; i++) { + /* Crude progress bar, enable with -VV */ + msg_pspew("."); + ret = usb_bulk_read(dediprog_handle, 0x80 | dediprog_endpoint, tmpbuf, bulktransfer_chunksize, DEFAULT_TIMEOUT); + memcpy(buf + residue + i * chunksize, tmpbuf, chunksize); + if (ret != chunksize) { + msg_perr("SPI bulk read %i failed, expected %i, got %i " + "%s!\n", i, chunksize, ret, usb_strerror()); + return 1; + } + /* ret was 512 if successful. Reset it. */ + ret = 0; + } + + len -= residue + i * chunksize; + if (len) { + msg_pdbg("Slow read for partial block from 0x%x, length 0x%x\n", + start, len); + ret = spi_read_chunked(flash, buf + residue + i * chunksize, + start + residue + i * chunksize, len, 16); + if (ret) + return ret; + } + + return 0; }
int dediprog_spi_send_command(unsigned int writecnt, unsigned int readcnt, @@ -345,6 +405,7 @@ struct usb_device *dev; char *voltage; int millivolt = 3500; + int ret;
msg_pspew("%s\n", __func__);
@@ -371,8 +432,23 @@ dev->descriptor.idVendor, dev->descriptor.idProduct); dediprog_handle = usb_open(dev); - usb_set_configuration(dediprog_handle, 1); - usb_claim_interface(dediprog_handle, 0); + ret = usb_set_configuration(dediprog_handle, 1); + if (ret < 0) { + msg_perr("Could not set USB device configuration: %i %s\n", + ret, usb_strerror()); + if (usb_close(dediprog_handle)) + msg_perr("Could not close USB device!\n"); + return 1; + } + ret = usb_claim_interface(dediprog_handle, 0); + if (ret < 0) { + msg_perr("Could not claim USB device interface %i: %i %s\n", + 0, ret, usb_strerror()); + if (usb_close(dediprog_handle)) + msg_perr("Could not close USB device!\n"); + return 1; + } + dediprog_endpoint = 2; /* URB 6. Command A. */ if (dediprog_command_a()) return 1; @@ -445,9 +521,9 @@ /* URB 136 is just URB 9. */ /* URB 137 is just URB 11. */
- /* Command I is probably Start Bulk Read. Data is u16 blockcount, u16 blocksize. */ - /* Command J is probably Start Bulk Write. Data is u16 blockcount, u16 blocksize. */ - /* Bulk transfer sizes for Command I/J are always 512 bytes, rest is filled with 0xff. */ + /* Command Start Bulk Read. Data is u16 blockcount, u16 blocksize. */ + /* Command Start Bulk Write. Data is u16 blockcount, u16 blocksize. */ + /* Bulk transfer sizes for Command Start Bulk Read/Write are always 512 bytes, rest is filled with 0xff. */
return 0; } @@ -461,8 +537,12 @@ if (dediprog_set_spi_voltage(0x0)) return 1;
+ if (usb_release_interface(dediprog_handle, 0)) { + msg_perr("Could not release USB interface!\n"); + return 1; + } if (usb_close(dediprog_handle)) { - msg_perr("Couldn't close USB device!\n"); + msg_perr("Could not close USB device!\n"); return 1; } return 0;