From 3285bd2bbe2647f164f17c7bc6b3a44fb16b093f Mon Sep 17 00:00:00 2001 From: Bjorn Andersson Date: Tue, 7 May 2024 14:30:09 -0500 Subject: [PATCH 1/2] usb: Replace USBDEVFS interface with libusb In order to support selecting board based on serial number the iProduct field needs to be inspected, with the hand-rolled parsing of the USB descriptors this becomes cumbersome. Furthermore the direct use of Linux's USBDEVFS creats an unnecessary dependency on the host OS being Linux. It's unclear why libusb wasn't choosen in the first place, perhaps it relates to the faint memory of 0.1 vs 1.0 packaging issues? Move to libusb-1.0 in order to resolve these issues, as well as clean up the code a bit. Signed-off-by: Bjorn Andersson --- Makefile | 4 +- README | 4 +- qdl.h | 3 + usb.c | 300 +++++++++++++++++++------------------------------------ 4 files changed, 109 insertions(+), 202 deletions(-) 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.h b/qdl.h index f5d89c5..7d2fb75 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; diff --git a/usb.c b/usb.c index 2e2adc8..0737db6 100644 --- a/usb.c +++ b/usb.c @@ -1,68 +1,47 @@ -#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 int qdl_parse_usb_descriptors(libusb_device *dev, struct qdl_device *qdl, int *intf) { - 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; + const struct libusb_endpoint_descriptor *endpoint; + const struct libusb_interface_descriptor *ifc; + struct libusb_config_descriptor *config; + struct libusb_device_descriptor desc; size_t out_size; size_t in_size; - void *ptr; - void *end; - char desc[1024]; - - n = read(fd, desc, sizeof(desc)); - if (n < 0) - return n; - - ptr = (void*)desc; - end = ptr + n; - - dev = ptr; + 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; + } /* Consider only devices with vid 0x0506 and product id 0x9008 or 0x900e */ - if (dev->idVendor != 0x05c6 || (dev->idProduct != 0x9008 && dev->idProduct != 0x900e)) - return -EINVAL; - - ptr += dev->bLength; - if (ptr >= end || dev->bDescriptorType != USB_DT_DEVICE) - return -EINVAL; - - cfg = ptr; - ptr += cfg->bLength; - if (ptr >= end || cfg->bDescriptorType != USB_DT_CONFIG) - return -EINVAL; - - for (k = 0; k < cfg->bNumInterfaces; k++) { - if (ptr >= end) - return -EINVAL; + 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 +49,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; - - do { - ept = ptr; - if (ept->bLength < USB_DT_ENDPOINT_SIZE) - return -EINVAL; + endpoint = &ifc->endpoint[l]; - 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,7 +76,6 @@ static int parse_usb_desc(int fd, struct qdl_device *qdl, int *intf) ifc->bInterfaceProtocol != 17) continue; - qdl->fd = fd; qdl->in_ep = in; qdl->out_ep = out; qdl->in_maxpktsize = in_size; @@ -121,171 +83,113 @@ static int parse_usb_desc(int fd, struct qdl_device *qdl, int *intf) *intf = ifc->bInterfaceNumber; - return 0; + return 1; } - return -ENOENT; + return 0; } int qdl_open(struct qdl_device *qdl) { - 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_handle *handle; + struct libusb_device **devs; + struct libusb_device *dev; + bool wait_printed = false; + int intf_num; + 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; + int i; - fd = open(dev_node, O_RDWR); - if (fd < 0) - continue; - - ret = parse_usb_desc(fd, qdl, &intf); - if (!ret) - goto found; - - close(fd); - } - - 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; - - if (!FD_ISSET(mon_fd, &rfds)) - continue; - - dev = udev_monitor_receive_device(mon); - dev_node = udev_device_get_devnode(dev); - - if (!dev_node) - continue; + n = libusb_get_device_list(NULL, &devs); + if (n < 0) + err(1, "failed to list USB devices"); - fd = open(dev_node, O_RDWR); - if (fd < 0) - continue; + for (i = 0; devs[i]; i++) { + dev = devs[i]; - ret = parse_usb_desc(fd, qdl, &intf); - if (!ret) - goto found; + ret = qdl_parse_usb_descriptors(dev, qdl, &intf_num); + if (ret != 1) + continue; - close(fd); - } + ret = libusb_open(dev, &handle); + if (ret < 0) { + warnx("unable to open USB device"); + continue; + } - udev_enumerate_unref(enumerate); - udev_monitor_unref(mon); - udev_unref(udev); + ret = libusb_claim_interface(handle, intf_num); + if (ret < 0) { + warnx("failed to claim USB interface"); + libusb_close(handle); + } - return -ENOENT; + qdl->usb_handle = handle; + break; + } -found: - udev_enumerate_unref(enumerate); - udev_monitor_unref(mon); - udev_unref(udev); + libusb_free_device_list(devs, 1); - cmd.ifno = intf; - cmd.ioctl_code = USBDEVFS_DISCONNECT; - cmd.data = NULL; + if (qdl->usb_handle) + return 0; - ret = ioctl(qdl->fd, USBDEVFS_IOCTL, &cmd); - if (ret && errno != ENODATA) - err(1, "failed to disconnect kernel driver"); + if (!wait_printed) { + fprintf(stderr, "Waiting for EDL device\n"); + wait_printed = true; + } - ret = ioctl(qdl->fd, USBDEVFS_CLAIMINTERFACE, &intf); - if (ret < 0) - err(1, "failed to claim USB interface"); + usleep(250000); + } - 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; } - From 43976ff903795c1b6c748adf67aa21db587b6e57 Mon Sep 17 00:00:00 2001 From: Bjorn Andersson Date: Tue, 7 May 2024 15:08:57 -0500 Subject: [PATCH 2/2] usb: Allow selecting board by serial number When working on a host with multiple boards attached being able to select a specific board by serial number becomes necessary. In the EDL USB descriptors a device serial number is available as part of the iProduct string, so this can be used for comparison. As libusb requires a handle the libusb_open() needs to be moved into the loop. Signed-off-by: Bjorn Andersson --- qdl.c | 9 +++++-- qdl.h | 2 +- ramdump.c | 9 +++++-- usb.c | 80 ++++++++++++++++++++++++++++++++++++++----------------- 4 files changed, 71 insertions(+), 29 deletions(-) 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 7d2fb75..0bb310a 100644 --- a/qdl.h +++ b/qdl.h @@ -24,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 0737db6..35a1c1e 100644 --- a/usb.c +++ b/usb.c @@ -2,6 +2,7 @@ #include #include #include +#include #include #include #include @@ -9,12 +10,40 @@ #include "qdl.h" -static int qdl_parse_usb_descriptors(libusb_device *dev, 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) +{ + char buf[128]; + char *p; + int ret; + + /* If no serial is requested, consider everything a match */ + if (!serial) + return true; + + 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; + } + + p = strstr(buf, "_SN:"); + if (!p) + return false; + + p += strlen("_SN:"); + p[strcspn(p, " _")] = '\0'; + + return strcmp(p, serial) == 0; +} + +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; @@ -76,26 +105,42 @@ static int qdl_parse_usb_descriptors(libusb_device *dev, struct qdl_device *qdl, ifc->bInterfaceProtocol != 17) continue; + 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 1; } return 0; } -int qdl_open(struct qdl_device *qdl) +int qdl_open(struct qdl_device *qdl, const char *serial) { - struct libusb_device_handle *handle; struct libusb_device **devs; struct libusb_device *dev; bool wait_printed = false; - int intf_num; + bool found = false; ssize_t n; int ret; int i; @@ -112,29 +157,16 @@ int qdl_open(struct qdl_device *qdl) for (i = 0; devs[i]; i++) { dev = devs[i]; - ret = qdl_parse_usb_descriptors(dev, qdl, &intf_num); - if (ret != 1) - continue; - - ret = libusb_open(dev, &handle); - if (ret < 0) { - warnx("unable to open USB device"); - continue; + ret = qdl_try_open(dev, qdl, serial); + if (ret == 1) { + found = true; + break; } - - ret = libusb_claim_interface(handle, intf_num); - if (ret < 0) { - warnx("failed to claim USB interface"); - libusb_close(handle); - } - - qdl->usb_handle = handle; - break; } libusb_free_device_list(devs, 1); - if (qdl->usb_handle) + if (found) return 0; if (!wait_printed) {