diff --git a/Makefile b/Makefile index 6bef1bc..2289897 100644 --- a/Makefile +++ b/Makefile @@ -1,8 +1,8 @@ QDL := qdl RAMDUMP := qdl-ramdump -CFLAGS := -O2 -Wall -g `pkg-config --cflags libxml-2.0` -LDFLAGS := `pkg-config --libs libxml-2.0 libudev` +CFLAGS := -O2 -Wall -g `pkg-config --cflags libxml-2.0 libusb-1.0` +LDFLAGS := `pkg-config --libs libxml-2.0 libusb-1.0` prefix := /usr/local QDL_SRCS := firehose.c qdl.c sahara.c util.c patch.c program.c ufs.c usb.c diff --git a/README b/README index 7bb9cf3..29aeba0 100644 --- a/README +++ b/README @@ -9,8 +9,8 @@ Usage: Building ======== -In order to build the project you need libxml2 and libudev headers -and libraries, found in e.g. the libxml2-dev and libudev-dev packages +In order to build the project you need libxml2 and libusb-1.0 headers +and libraries, found in e.g. the libxml2-dev and libusb-1.0.0-dev packages With these installed run: make diff --git a/qdl.c b/qdl.c index df84401..e15dcbd 100644 --- a/qdl.c +++ b/qdl.c @@ -105,6 +105,7 @@ int main(int argc, char **argv) { char *prog_mbn, *storage="ufs"; char *incdir = NULL; + char *serial = NULL; int type; int ret; int opt; @@ -115,11 +116,12 @@ int main(int argc, char **argv) {"debug", no_argument, 0, 'd'}, {"include", required_argument, 0, 'i'}, {"finalize-provisioning", no_argument, 0, 'l'}, + {"serial", required_argument, 0, 'S'}, {"storage", required_argument, 0, 's'}, {0, 0, 0, 0} }; - while ((opt = getopt_long(argc, argv, "di:", options, NULL )) != -1) { + while ((opt = getopt_long(argc, argv, "di:S:", options, NULL )) != -1) { switch (opt) { case 'd': qdl_debug = true; @@ -133,6 +135,9 @@ int main(int argc, char **argv) case 's': storage = optarg; break; + case 'S': + serial = optarg; + break; default: print_usage(); return 1; @@ -174,7 +179,7 @@ int main(int argc, char **argv) } } while (++optind < argc); - ret = qdl_open(&qdl); + ret = qdl_open(&qdl, serial); if (ret) return 1; diff --git a/qdl.h b/qdl.h index f5d89c5..0bb310a 100644 --- a/qdl.h +++ b/qdl.h @@ -9,7 +9,10 @@ #define MAPPING_SZ 64 +struct libusb_device_handle; + struct qdl_device { + struct libusb_device_handle *usb_handle; int fd; int in_ep; @@ -21,7 +24,7 @@ struct qdl_device { char *mappings[MAPPING_SZ]; // array index is the id from the device }; -int qdl_open(struct qdl_device *qdl); +int qdl_open(struct qdl_device *qdl, const char *serial); int qdl_read(struct qdl_device *qdl, void *buf, size_t len, unsigned int timeout); int qdl_write(struct qdl_device *qdl, const void *buf, size_t len); diff --git a/ramdump.c b/ramdump.c index d06cdb9..c094c5f 100644 --- a/ramdump.c +++ b/ramdump.c @@ -21,16 +21,18 @@ int main(int argc, char **argv) struct qdl_device qdl; char *ramdump_path = "."; char *filter = NULL; + char *serial = NULL; int ret; int opt; static struct option options[] = { {"debug", no_argument, 0, 'd'}, {"output", required_argument, 0, 'o'}, + {"serial", required_argument, 0, 'S'}, {0, 0, 0, 0} }; - while ((opt = getopt_long(argc, argv, "do:", options, NULL )) != -1) { + while ((opt = getopt_long(argc, argv, "do:S:", options, NULL )) != -1) { switch (opt) { case 'd': qdl_debug = true; @@ -38,6 +40,9 @@ int main(int argc, char **argv) case 'o': ramdump_path = optarg; break; + case 'S': + serial = optarg; + break; default: print_usage(); } @@ -49,7 +54,7 @@ int main(int argc, char **argv) if (optind != argc) print_usage(); - ret = qdl_open(&qdl); + ret = qdl_open(&qdl, serial); if (ret) return 1; diff --git a/usb.c b/usb.c index 2e2adc8..35a1c1e 100644 --- a/usb.c +++ b/usb.c @@ -1,68 +1,76 @@ -#include -#include #include #include #include -#include #include -#include +#include +#include #include #include +#include #include "qdl.h" -static int parse_usb_desc(int fd, struct qdl_device *qdl, int *intf) +static bool qdl_match_usb_serial(struct libusb_device_handle *handle, const char *serial, + const struct libusb_device_descriptor *desc) { - const struct usb_interface_descriptor *ifc; - const struct usb_endpoint_descriptor *ept; - const struct usb_device_descriptor *dev; - const struct usb_config_descriptor *cfg; - const struct usb_descriptor_header *hdr; - unsigned type; - unsigned out; - unsigned in; - unsigned k; - unsigned l; - ssize_t n; - size_t out_size; - size_t in_size; - void *ptr; - void *end; - char desc[1024]; + char buf[128]; + char *p; + int ret; - n = read(fd, desc, sizeof(desc)); - if (n < 0) - return n; + /* If no serial is requested, consider everything a match */ + if (!serial) + return true; - ptr = (void*)desc; - end = ptr + n; + ret = libusb_get_string_descriptor_ascii(handle, desc->iProduct, (unsigned char *)buf, sizeof(buf)); + if (ret < 0) { + warnx("failed to read iProduct descriptor: %s", libusb_strerror(ret)); + return false; + } - dev = ptr; + p = strstr(buf, "_SN:"); + if (!p) + return false; - /* Consider only devices with vid 0x0506 and product id 0x9008 or 0x900e */ - if (dev->idVendor != 0x05c6 || (dev->idProduct != 0x9008 && dev->idProduct != 0x900e)) - return -EINVAL; + p += strlen("_SN:"); + p[strcspn(p, " _")] = '\0'; - ptr += dev->bLength; - if (ptr >= end || dev->bDescriptorType != USB_DT_DEVICE) - return -EINVAL; + return strcmp(p, serial) == 0; +} - cfg = ptr; - ptr += cfg->bLength; - if (ptr >= end || cfg->bDescriptorType != USB_DT_CONFIG) - return -EINVAL; +static int qdl_try_open(libusb_device *dev, struct qdl_device *qdl, const char *serial) +{ + const struct libusb_endpoint_descriptor *endpoint; + const struct libusb_interface_descriptor *ifc; + struct libusb_config_descriptor *config; + struct libusb_device_descriptor desc; + struct libusb_device_handle *handle; + size_t out_size; + size_t in_size; + uint8_t type; + int ret; + int out; + int in; + int k; + int l; + + ret = libusb_get_device_descriptor(dev, &desc); + if (ret < 0) { + warnx("failed to get USB device descriptor"); + return -1; + } - for (k = 0; k < cfg->bNumInterfaces; k++) { - if (ptr >= end) - return -EINVAL; + /* Consider only devices with vid 0x0506 and product id 0x9008 or 0x900e */ + if (desc.idVendor != 0x05c6 || (desc.idProduct != 0x9008 && desc.idProduct != 0x900e)) + return 0; - do { - ifc = ptr; - if (ifc->bLength < USB_DT_INTERFACE_SIZE) - return -EINVAL; + ret = libusb_get_active_config_descriptor(dev, &config); + if (ret < 0) { + warnx("failed to acquire USB device's active config descriptor"); + return -1; + } - ptr += ifc->bLength; - } while (ptr < end && ifc->bDescriptorType != USB_DT_INTERFACE); + for (k = 0; k < config->bNumInterfaces; k++) { + ifc = config->interface[k].altsetting; in = -1; out = -1; @@ -70,35 +78,19 @@ static int parse_usb_desc(int fd, struct qdl_device *qdl, int *intf) out_size = 0; for (l = 0; l < ifc->bNumEndpoints; l++) { - if (ptr >= end) - return -EINVAL; + endpoint = &ifc->endpoint[l]; - do { - ept = ptr; - if (ept->bLength < USB_DT_ENDPOINT_SIZE) - return -EINVAL; - - ptr += ept->bLength; - } while (ptr < end && ept->bDescriptorType != USB_DT_ENDPOINT); - - type = ept->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; - if (type != USB_ENDPOINT_XFER_BULK) + type = endpoint->bmAttributes & LIBUSB_TRANSFER_TYPE_MASK; + if (type != LIBUSB_ENDPOINT_TRANSFER_TYPE_BULK) continue; - if (ept->bEndpointAddress & USB_DIR_IN) { - in = ept->bEndpointAddress; - in_size = ept->wMaxPacketSize; + if (endpoint->bEndpointAddress & LIBUSB_ENDPOINT_IN) { + in = endpoint->bEndpointAddress; + in_size = endpoint->wMaxPacketSize; } else { - out = ept->bEndpointAddress; - out_size = ept->wMaxPacketSize; + out = endpoint->bEndpointAddress; + out_size = endpoint->wMaxPacketSize; } - - if (ptr >= end) - break; - - hdr = ptr; - if (hdr->bDescriptorType == USB_DT_SS_ENDPOINT_COMP) - ptr += USB_DT_SS_EP_COMP_SIZE; } if (ifc->bInterfaceClass != 0xff) @@ -113,179 +105,123 @@ static int parse_usb_desc(int fd, struct qdl_device *qdl, int *intf) ifc->bInterfaceProtocol != 17) continue; - qdl->fd = fd; + ret = libusb_open(dev, &handle); + if (ret < 0) { + warnx("unable to open USB device"); + continue; + } + + if (!qdl_match_usb_serial(handle, serial, &desc)) { + libusb_close(handle); + continue; + } + + ret = libusb_claim_interface(handle, ifc->bInterfaceNumber); + if (ret < 0) { + warnx("failed to claim USB interface"); + libusb_close(handle); + continue; + } + + qdl->usb_handle = handle; qdl->in_ep = in; qdl->out_ep = out; qdl->in_maxpktsize = in_size; qdl->out_maxpktsize = out_size; - *intf = ifc->bInterfaceNumber; - - return 0; + return 1; } - return -ENOENT; + return 0; } -int qdl_open(struct qdl_device *qdl) +int qdl_open(struct qdl_device *qdl, const char *serial) { - struct udev_enumerate *enumerate; - struct udev_list_entry *devices; - struct udev_list_entry *dev_list_entry; - struct udev_monitor *mon; - struct udev_device *dev; - const char *dev_node; - struct udev *udev; - const char *path; - struct usbdevfs_ioctl cmd; - int mon_fd; - int intf = -1; + struct libusb_device **devs; + struct libusb_device *dev; + bool wait_printed = false; + bool found = false; + ssize_t n; int ret; - int fd; - - udev = udev_new(); - if (!udev) - err(1, "failed to initialize udev"); - - mon = udev_monitor_new_from_netlink(udev, "udev"); - udev_monitor_filter_add_match_subsystem_devtype(mon, "usb", NULL); - udev_monitor_enable_receiving(mon); - mon_fd = udev_monitor_get_fd(mon); - - enumerate = udev_enumerate_new(udev); - udev_enumerate_add_match_subsystem(enumerate, "usb"); - udev_enumerate_scan_devices(enumerate); - devices = udev_enumerate_get_list_entry(enumerate); - - udev_list_entry_foreach(dev_list_entry, devices) { - path = udev_list_entry_get_name(dev_list_entry); - dev = udev_device_new_from_syspath(udev, path); - dev_node = udev_device_get_devnode(dev); - - if (!dev_node) - continue; - - fd = open(dev_node, O_RDWR); - if (fd < 0) - continue; - - ret = parse_usb_desc(fd, qdl, &intf); - if (!ret) - goto found; - - close(fd); - } + int i; - fprintf(stderr, "Waiting for EDL device\n"); + ret = libusb_init(NULL); + if (ret < 0) + err(1, "failed to initialize libusb"); for (;;) { - fd_set rfds; - - FD_ZERO(&rfds); - FD_SET(mon_fd, &rfds); - - ret = select(mon_fd + 1, &rfds, NULL, NULL, NULL); - if (ret < 0) - return -1; + n = libusb_get_device_list(NULL, &devs); + if (n < 0) + err(1, "failed to list USB devices"); - if (!FD_ISSET(mon_fd, &rfds)) - continue; + for (i = 0; devs[i]; i++) { + dev = devs[i]; - dev = udev_monitor_receive_device(mon); - dev_node = udev_device_get_devnode(dev); + ret = qdl_try_open(dev, qdl, serial); + if (ret == 1) { + found = true; + break; + } + } - if (!dev_node) - continue; + libusb_free_device_list(devs, 1); - fd = open(dev_node, O_RDWR); - if (fd < 0) - continue; + if (found) + return 0; - ret = parse_usb_desc(fd, qdl, &intf); - if (!ret) - goto found; + if (!wait_printed) { + fprintf(stderr, "Waiting for EDL device\n"); + wait_printed = true; + } - close(fd); + usleep(250000); } - udev_enumerate_unref(enumerate); - udev_monitor_unref(mon); - udev_unref(udev); - - return -ENOENT; - -found: - udev_enumerate_unref(enumerate); - udev_monitor_unref(mon); - udev_unref(udev); - - cmd.ifno = intf; - cmd.ioctl_code = USBDEVFS_DISCONNECT; - cmd.data = NULL; - - ret = ioctl(qdl->fd, USBDEVFS_IOCTL, &cmd); - if (ret && errno != ENODATA) - err(1, "failed to disconnect kernel driver"); - - ret = ioctl(qdl->fd, USBDEVFS_CLAIMINTERFACE, &intf); - if (ret < 0) - err(1, "failed to claim USB interface"); - - return 0; + return -1; } int qdl_read(struct qdl_device *qdl, void *buf, size_t len, unsigned int timeout) { - struct usbdevfs_bulktransfer bulk = {}; + int actual; + int ret; - bulk.ep = qdl->in_ep; - bulk.len = len; - bulk.data = buf; - bulk.timeout = timeout; + ret = libusb_bulk_transfer(qdl->usb_handle, qdl->in_ep, buf, len, &actual, timeout); + if (ret < 0) + return -1; - return ioctl(qdl->fd, USBDEVFS_BULK, &bulk); + return actual; } int qdl_write(struct qdl_device *qdl, const void *buf, size_t len) { - unsigned char *data = (unsigned char*) buf; - struct usbdevfs_bulktransfer bulk = {}; - unsigned count = 0; + unsigned int count = 0; size_t len_orig = len; - int n; + int actual; + int xfer; + int ret; - while(len > 0) { - int xfer; + while (len > 0) { xfer = (len > qdl->out_maxpktsize) ? qdl->out_maxpktsize : len; - bulk.ep = qdl->out_ep; - bulk.len = xfer; - bulk.data = data; - bulk.timeout = 1000; - - n = ioctl(qdl->fd, USBDEVFS_BULK, &bulk); - if(n != xfer) { - fprintf(stderr, "ERROR: n = %d, errno = %d (%s)\n", - n, errno, strerror(errno)); + ret = libusb_bulk_transfer(qdl->usb_handle, qdl->out_ep, data, + xfer, &actual, 1000); + if (ret < 0) { + warnx("bulk write failed: %s", libusb_strerror(ret)); return -1; } - count += xfer; - len -= xfer; - data += xfer; + + count += actual; + len -= actual; + data += actual; } if (len_orig % qdl->out_maxpktsize == 0) { - bulk.ep = qdl->out_ep; - bulk.len = 0; - bulk.data = NULL; - bulk.timeout = 1000; - - n = ioctl(qdl->fd, USBDEVFS_BULK, &bulk); - if (n < 0) - return n; + ret = libusb_bulk_transfer(qdl->usb_handle, qdl->out_ep, NULL, + 0, &actual, 1000); + if (ret < 0) + return -1; } return count; } -