migrate to libusb-1 by me (Mike Frysinger) workaround kernel/libusb transfer sizes by Julius Werner --- a/Makefile +++ b/Makefile @@ -10,8 +10,8 @@ CFLAGS ?= -O2 -g CFLAGS += -Wall PKG_CONFIG ?= pkg-config -CPPFLAGS += $(shell $(PKG_CONFIG) --cflags libusb) -LDLIBS = $(shell $(PKG_CONFIG) --libs libusb) +CPPFLAGS += $(shell $(PKG_CONFIG) --cflags libusb-1.0) +LDLIBS = $(shell $(PKG_CONFIG) --libs libusb-1.0) all: dltool --- a/dltool.c +++ b/dltool.c @@ -7,15 +7,25 @@ #include #include +#include #include #include #include #include #include +#include +#include -#include +#include + +/* The kernel USB layer is restrictive in the max size in a single URB. + * When we exceed that, the kernel throws ENOMEM, but the libusb layer + * doesn't handle things gracefully. So manually split up into chunks + * ourselves so we can avoid that failure case. + */ +#define CHUNKSIZE (1 << 18) unsigned int debug = 0; unsigned long dl_addr = 0x30000000L; @@ -23,12 +33,22 @@ unsigned long dl_size = 0L; unsigned char *dl_data = NULL; char *dl_file = "download.dat"; -char *dl_udev = NULL; -char *dl_ubus = NULL; +libusb_context *ctx = NULL; +libusb_device_handle *devh = NULL; int ep_out = 0; #define DBG(x) if (debug) { printf x; } +#define err(fmt, args...) \ + do { \ + if (devh) \ + libusb_close(devh); \ + if (ctx) \ + libusb_exit(ctx); \ + fprintf(stderr, "dltool: " fmt "\n", ## args); \ + exit(1); \ + } while (0) +#define errp(fmt, args...) err(fmt ": %s", ## args, strerror(errno)) void write_u32(unsigned char *dp, unsigned long val) { @@ -108,25 +128,30 @@ void calc_cksum(unsigned char *data, ssize_t len) cp[1] = cksum >> 8; } -int verify_device(struct usb_device *dev) +int verify_device(libusb_device *dev) { + struct libusb_device_descriptor desc; + + if (libusb_get_device_descriptor(dev, &desc)) + return 0; + DBG(("dev %p: configurations %d\n", - dev, dev->descriptor.bNumConfigurations)); + dev, desc.bNumConfigurations)); - if (dev->descriptor.bNumConfigurations != 1) + if (desc.bNumConfigurations != 1) return 0; - DBG(("\t=> bLength %d\n", dev->descriptor.bLength)); - DBG(("\t=> bType %d\n", dev->descriptor.bDescriptorType)); - DBG(("\t=> bcdUSB %x\n", dev->descriptor.bcdUSB)); - DBG(("\t=> idVendor %x\n", dev->descriptor.idVendor)); - DBG(("\t=> idProduct %x\n", dev->descriptor.idProduct)); + DBG(("\t=> bLength %d\n", desc.bLength)); + DBG(("\t=> bType %d\n", desc.bDescriptorType)); + DBG(("\t=> bcdUSB %x\n", desc.bcdUSB)); + DBG(("\t=> idVendor %x\n", desc.idVendor)); + DBG(("\t=> idProduct %x\n", desc.idProduct)); - if (dev->descriptor.idVendor == 0x5345 && dev->descriptor.idProduct == 0x1234) { + if (desc.idVendor == 0x5345 && desc.idProduct == 0x1234) { ep_out = 3; return 1; } - else if(dev->descriptor.idVendor == 0x4e8 && dev->descriptor.idProduct == 0x1234){ + else if(desc.idVendor == 0x4e8 && desc.idProduct == 0x1234){ printf("S3C64XX Detected!\n"); ep_out = 2; return 1; @@ -173,6 +198,12 @@ struct option long_opts[] = { .val = 'x', }, { + .name = "help", + .has_arg = 0, + .flag = NULL, + .val = 'h', + }, + { .name = NULL } }; @@ -181,12 +212,14 @@ int flg_show = 0; int main(int argc, char **argv) { - struct usb_bus *bus, *busp; - struct usb_device *result = NULL; - struct usb_device *found = NULL; + ssize_t num_devs, i; + libusb_device **list; + libusb_device *found; + int dl_ubus = -1; + int dl_udev = -1; + uint8_t bus_num, dev_num; unsigned long fsize; - usb_dev_handle *devh; - int ret; + int ret, transferred; printf("SMDK42XX,S3C64XX USB Download Tool\n"); printf("Version 0.20 (c) 2004,2005,2006" @@ -197,7 +230,7 @@ int main(int argc, char **argv) int index = 0; int c; - c = getopt_long(argc, argv, "a:b:d:f:s", long_opts, &index); + c = getopt_long(argc, argv, "a:b:d:f:shx", long_opts, &index); DBG(("option index %d\n",c )); @@ -218,117 +251,134 @@ int main(int argc, char **argv) break; case 'b': - dl_ubus = optarg; + dl_ubus = atoi(optarg); break; case 'd': - dl_udev = optarg; + dl_udev = atoi(optarg); break; case 'x': debug = 1; + break; + + case 'h': + puts( + "Usage: dltool [options]\n" + "\n" + "-a \n" + "-b \n" + "-d \n" + "-f \n" + "-s Show found devices\n" + "-x Enable debug\n" + ); + return 0; } } - usb_init(); - usb_find_busses(); - usb_find_devices(); - - bus = usb_get_busses(); - - DBG(("usb_get_busses: %p\n", bus)); - - for (busp = bus; busp != NULL; busp = busp->next) { - struct usb_device *dev; - - DBG(("bus %p: dirname %s\n", busp, busp->dirname)); - - if (dl_ubus) { - if (strcmp(busp->dirname, dl_ubus) != 0) - continue; - } + ret = libusb_init(&ctx); + if (ret) + errp("could not initialize usb stack"); - for (dev = busp->devices; dev != NULL; dev = dev->next) { - DBG(("dev %p filename %s\n", dev, dev->filename)); + bus_num = dev_num = 0; + found = NULL; + num_devs = libusb_get_device_list(ctx, &list); + for (i = 0; i < num_devs; ++i) { + libusb_device *dev = list[i]; + bus_num = libusb_get_bus_number(dev); + dev_num = libusb_get_device_address(dev); - if (!verify_device(dev)) - continue; + DBG(("bus %u; dev %u (%p)\n", bus_num, dev_num, dev)); - if (flg_show) { - printf("bus %s: device %s\n", - busp->dirname, dev->filename); - continue; - } + if (dl_ubus >= 0 && bus_num != dl_ubus) + continue; - found = dev; + if (!verify_device(dev)) + continue; - if (dl_udev) { - if (strcmp(dev->filename, dl_udev) == 0) { - result = dev; - break; - } - } + if (flg_show) { + printf("bus %u: device %u\n", bus_num, dev_num); + continue; } - if (result != NULL) - break; + if (dl_udev >= 0 && dev_num != dl_udev) + continue; + + found = dev; + break; } if (flg_show) return 0; - DBG(("device %p, found %p\n", result, found)); + DBG(("found %p\n", found)); - if (result == NULL && found != NULL) - result = found; - - if (result == NULL) { - fprintf(stderr, "failed to find device\n"); - return 1; - } + if (found == NULL) + err("failed to find device\n"); - printf("=> found device: bus %s, dev %s\n", - result->bus->dirname, result->filename); + printf("=> found device: bus %u, dev %u\n", + bus_num, dev_num); dl_data = load_file(dl_file, &dl_size, &fsize); - if (dl_data == NULL) { - printf("failed to load %s\n", dl_file); - return 1; - } + if (dl_data == NULL) + errp("failed to load %s", dl_file); printf("=> loaded %ld bytes from %s\n", fsize, dl_file); - devh = usb_open(result); - if (devh == NULL) { - perror("usb_open"); - return 1; - } + ret = libusb_open(found, &devh); + if (ret == 0) { + /* + * Seems to break some recovery modes :( + * http://crosbug.com/26083 + * These fail: + * smdk-dltool -a 0x02021400 -f bl1.bin + * smdk-dltool -a 0x02023400 -f bl2.bin + */ +#if 0 + uint8_t configuration; + struct libusb_config_descriptor *config; + libusb_get_active_config_descriptor(found, &config); + configuration = config->bConfigurationValue; + libusb_free_config_descriptor(config); + libusb_set_configuration(devh, configuration); +#endif + } else + errp("libusb_open"); DBG(("claim interface\n")); - if (usb_claim_interface(devh, 0) < 0) { - perror("usb_claim_interface"); - usb_close(devh); - return 1; - } + ret = libusb_claim_interface(devh, 0); + if (ret) + errp("libusb_claim_interface"); printf("=> Downloading %ld bytes to 0x%08lx\n", dl_size, dl_addr); write_header(dl_data, dl_addr, dl_size); calc_cksum(dl_data, dl_size); - //ret = usb_bulk_write(devh, 3, (void *)dl_data, dl_size, 5*1000*1000); - ret = usb_bulk_write(devh, ep_out, (void *)dl_data, dl_size, 5*1000*1000); - printf("=> usb_bulk_write() returned %d\n", ret); - - if (ret != dl_size) { - printf("failed to write %ld bytes\n", dl_size); + transferred = 0; + while (transferred < dl_size) { + int actual, expected = MIN(dl_size - transferred, CHUNKSIZE); + ret = libusb_bulk_transfer(devh, ep_out, dl_data + transferred, + expected, &actual, 5 * 1000 * 1000); + printf("=> usb_bulk_write(%d) returned %d, wrote %d\n", + expected, ret, actual); + transferred += actual; + + if (ret || (expected != actual)) { + printf("failed to write %ld bytes (wrote %d): %s\n", + dl_size, transferred, strerror(errno)); + ret = 1; + break; + } } free(dl_data); - usb_release_interface(devh, 0); - usb_close(devh); + libusb_release_interface(devh, 0); + libusb_close(devh); + libusb_exit(ctx); - return 0; + return ret; }