Package: libusb
Version: 2:0.1.12-25
Severity: normal
Tags: patch
User: ubuntu-de...@lists.ubuntu.com
Usertags: origin-ubuntu wily ubuntu-patch

Dear Maintainer,

I'm trying to make g15daemon correctly recover after suspend/resume and found 
that it occasionally hangs after resume in libusb's usb_urb_transfer() 
(linux.c) on this line:
ioctl(dev->fd, IOCTL_USB_REAPURB, &context);

After some research I found that linux kernel internally has lists of pending 
and completed requests. All requests - successful or not - are moved from 
pending list to completed list, no requests should be lost. But it looks like 
after suspend/resume some request may be lost (may be a kernel bug). It that 
case IOCTL_USB_REAPURB would hang forever since request it's waiting for will 
never appear on completed list.

There is simple workaround. After preceding IOCTL_USB_DISCARDURB call our 
request is guaranteed to be completed, so there is no need to use blocking 
IOCTL_USB_DISCARDURB, we may use non-blocking IOCTL_USB_REAPURBNDELAY instead.

I've attached patch with this workaround. It also fixes race condition existed 
in original code, which may cause corruption of stack.

  * Added debian/patches/12_hang_after_resume.diff to fix occasional hang
    after suspend/resume. (LP: #1455924)


Thanks for considering the patch.


-- System Information:
Debian Release: jessie/sid
  APT prefers vivid-updates
  APT policy: (500, 'vivid-updates'), (500, 'vivid-security'), (500, 'vivid'), 
(100, 'vivid-backports')
Architecture: amd64 (x86_64)
Foreign Architectures: i386

Kernel: Linux 3.19.0-16-generic (SMP w/4 CPU cores)
Locale: LANG=ru_RU.utf8, LC_CTYPE=ru_RU.utf8 (charmap=UTF-8)
Shell: /bin/sh linked to /bin/bash
Init: systemd (via /run/systemd/system)
=== added directory '.pc/12_hang_after_resume.diff'
=== added file '.pc/12_hang_after_resume.diff/linux.c'
--- .pc/12_hang_after_resume.diff/linux.c	1970-01-01 00:00:00 +0000
+++ .pc/12_hang_after_resume.diff/linux.c	2015-05-17 14:25:02 +0000
@@ -0,0 +1,742 @@
+/*
+ * Linux USB support
+ *
+ * Copyright (c) 2000-2003 Johannes Erdfelt <johan...@erdfelt.com>
+ *
+ * This library is covered by the LGPL, read LICENSE for details.
+ */
+
+#include <stdlib.h>	/* getenv, etc */
+#include <unistd.h>
+#include <string.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/time.h>
+#include <dirent.h>
+
+#include "linux.h"
+#include "usbi.h"
+
+static char usb_path[PATH_MAX + 1] = "";
+
+static int device_open(struct usb_device *dev)
+{
+  char filename[PATH_MAX + 1];
+  int fd;
+
+  snprintf(filename, sizeof(filename) - 1, "%s/%s/%s",
+    usb_path, dev->bus->dirname, dev->filename);
+
+  fd = open(filename, O_RDWR);
+  if (fd < 0) {
+    fd = open(filename, O_RDONLY);
+    if (fd < 0)
+      USB_ERROR_STR(-errno, "failed to open %s: %s",
+	filename, strerror(errno));
+  }
+
+  return fd;
+}
+
+int usb_os_open(usb_dev_handle *dev)
+{
+  dev->fd = device_open(dev->device);
+
+  return 0;
+}
+
+int usb_os_close(usb_dev_handle *dev)
+{
+  if (dev->fd < 0)
+    return 0;
+
+  if (close(dev->fd) == -1)
+    /* Failing trying to close a file really isn't an error, so return 0 */
+    USB_ERROR_STR(0, "tried to close device fd %d: %s", dev->fd,
+	strerror(errno));
+
+  return 0;
+}
+
+int usb_set_configuration(usb_dev_handle *dev, int configuration)
+{
+  int ret;
+
+  ret = ioctl(dev->fd, IOCTL_USB_SETCONFIG, &configuration);
+  if (ret < 0)
+    USB_ERROR_STR(-errno, "could not set config %d: %s", configuration,
+	strerror(errno));
+
+  dev->config = configuration;
+
+  return 0;
+}
+
+int usb_claim_interface(usb_dev_handle *dev, int interface)
+{
+  int ret;
+
+  ret = ioctl(dev->fd, IOCTL_USB_CLAIMINTF, &interface);
+  if (ret < 0) {
+    if (errno == EBUSY && usb_debug > 0)
+      fprintf(stderr, "Check that you have permissions to write to %s/%s and, if you don't, that you set up hotplug (http://linux-hotplug.sourceforge.net/) correctly.\n", dev->bus->dirname, dev->device->filename);
+
+    USB_ERROR_STR(-errno, "could not claim interface %d: %s", interface,
+	strerror(errno));
+  }
+
+  dev->interface = interface;
+
+  return 0;
+}
+
+int usb_release_interface(usb_dev_handle *dev, int interface)
+{
+  int ret;
+
+  ret = ioctl(dev->fd, IOCTL_USB_RELEASEINTF, &interface);
+  if (ret < 0)
+    USB_ERROR_STR(-errno, "could not release intf %d: %s", interface,
+    	strerror(errno));
+
+  dev->interface = -1;
+
+  return 0;
+}
+
+int usb_set_altinterface(usb_dev_handle *dev, int alternate)
+{
+  int ret;
+  struct usb_setinterface setintf;
+
+  if (dev->interface < 0)
+    USB_ERROR(-EINVAL);
+
+  setintf.interface = dev->interface;
+  setintf.altsetting = alternate;
+
+  ret = ioctl(dev->fd, IOCTL_USB_SETINTF, &setintf);
+  if (ret < 0)
+    USB_ERROR_STR(-errno, "could not set alt intf %d/%d: %s",
+	dev->interface, alternate, strerror(errno));
+
+  dev->altsetting = alternate;
+
+  return 0;
+}
+
+/*
+ * Linux usbfs has a limit of one page size for synchronous bulk read/write.
+ * 4096 is the most portable maximum we can do for now.
+ * Linux usbfs has a limit of 16KB for the URB interface. We use this now
+ * to get better performance for USB 2.0 devices.
+ */
+#define MAX_READ_WRITE	(16 * 1024)
+
+int usb_control_msg(usb_dev_handle *dev, int requesttype, int request,
+	int value, int index, char *bytes, int size, int timeout)
+{
+  struct usb_ctrltransfer ctrl;
+  int ret;
+
+  ctrl.bRequestType = requesttype;
+  ctrl.bRequest = request;
+  ctrl.wValue = value;
+  ctrl.wIndex = index;
+  ctrl.wLength = size;
+
+  ctrl.data = bytes;
+  ctrl.timeout = timeout;
+
+  ret = ioctl(dev->fd, IOCTL_USB_CONTROL, &ctrl);
+  if (ret < 0)
+    USB_ERROR_STR(-errno, "error sending control message: %s", strerror(errno));
+
+  return ret;
+}
+
+#define URB_USERCONTEXT_COOKIE		((void *)0x1)
+
+/* Reading and writing are the same except for the endpoint */
+static int usb_urb_transfer(usb_dev_handle *dev, int ep, int urbtype,
+	char *bytes, int size, int timeout)
+{
+  struct usb_urb urb;
+  int bytesdone = 0, requested;
+  struct timeval tv, tv_ref, tv_now;
+  struct usb_urb *context;
+  int ret, waiting;
+
+  /*
+   * HACK: The use of urb.usercontext is a hack to get threaded applications
+   * sort of working again. Threaded support is still not recommended, but
+   * this should allow applications to work in the common cases. Basically,
+   * if we get the completion for an URB we're not waiting for, then we update
+   * the usercontext pointer to 1 for the other threads URB and it will see
+   * the change after it wakes up from the the timeout. Ugly, but it works.
+   */
+
+  /*
+   * Get actual time, and add the timeout value. The result is the absolute
+   * time where we have to quit waiting for an message.
+   */
+  gettimeofday(&tv_ref, NULL);
+  tv_ref.tv_sec = tv_ref.tv_sec + timeout / 1000;
+  tv_ref.tv_usec = tv_ref.tv_usec + (timeout % 1000) * 1000;
+
+  if (tv_ref.tv_usec > 1000000) {
+    tv_ref.tv_usec -= 1000000;
+    tv_ref.tv_sec++;
+  }
+
+  waiting = 1;
+
+  do {
+    fd_set writefds;
+
+    requested = size - bytesdone;
+    if (requested > MAX_READ_WRITE)
+      requested = MAX_READ_WRITE;
+
+    urb.type = urbtype;
+    urb.endpoint = ep;
+    urb.flags = 0;
+    urb.buffer = bytes + bytesdone;
+    urb.buffer_length = requested;
+    urb.signr = 0;
+    urb.actual_length = 0;
+    urb.number_of_packets = 0;	/* don't do isochronous yet */
+    urb.usercontext = NULL;
+
+    ret = ioctl(dev->fd, IOCTL_USB_SUBMITURB, &urb);
+    if (ret < 0) {
+      USB_ERROR_STR(-errno, "error submitting URB: %s", strerror(errno));
+      return ret;
+    }
+
+restart:
+    context = NULL;
+    while (!urb.usercontext && ((ret = ioctl(dev->fd, IOCTL_USB_REAPURBNDELAY, &context)) == -1) && waiting) {
+      if (ret == -1)
+      {
+        if (errno == ENODEV)
+        {
+          return -ENODEV;
+        }
+      }
+      tv.tv_sec = 0;
+      tv.tv_usec = 1000; // 1 msec
+      FD_ZERO(&writefds);
+      FD_SET(dev->fd, &writefds);
+      select(dev->fd + 1, NULL, &writefds, NULL, &tv); //sub second wait
+
+      if (timeout) {
+        /* compare with actual time, as the select timeout is not that precise */
+        gettimeofday(&tv_now, NULL);
+
+        if ((tv_now.tv_sec > tv_ref.tv_sec) ||
+            ((tv_now.tv_sec == tv_ref.tv_sec) && (tv_now.tv_usec >= tv_ref.tv_usec)))
+          waiting = 0;
+      }
+    }
+
+    if (context && context != &urb) {
+      context->usercontext = URB_USERCONTEXT_COOKIE;
+      /* We need to restart since we got a successful URB, but not ours */
+      waiting = 1;
+      goto restart;
+    }
+
+    /*
+     * If there was an error, that wasn't EAGAIN (no completion), then
+     * something happened during the reaping and we should return that
+     * error now
+     */
+    if (ret < 0 && !urb.usercontext && errno != EAGAIN)
+      USB_ERROR_STR(-errno, "error reaping URB: %s", strerror(errno));
+
+    bytesdone += urb.actual_length;
+  } while ((ret == 0 || urb.usercontext) && bytesdone < size && urb.actual_length == requested && waiting);
+
+  /* If the URB didn't complete in success or error, then let's unlink it */
+  if ((ret < 0 && !urb.usercontext) || (!waiting && bytesdone < size)) {
+    int rc;
+
+    if (!waiting)
+      rc = -ETIMEDOUT;
+    else
+      rc = urb.status;
+
+    ret = ioctl(dev->fd, IOCTL_USB_DISCARDURB, &urb);
+    if (ret < 0 && errno != EINVAL && usb_debug >= 1)
+      fprintf(stderr, "error discarding URB: %s", strerror(errno));
+
+    /*
+     * When the URB is unlinked, it gets moved to the completed list and
+     * then we need to reap it or else the next time we call this function,
+     * we'll get the previous completion and exit early
+     */
+    ioctl(dev->fd, IOCTL_USB_REAPURB, &context);
+
+    return rc;
+  }
+
+  return bytesdone;
+}
+
+int usb_bulk_write(usb_dev_handle *dev, int ep, const char *bytes, int size,
+	int timeout)
+{
+  /* Ensure the endpoint address is correct */
+  return usb_urb_transfer(dev, ep, USB_URB_TYPE_BULK, (char *)bytes, size,
+		timeout);
+}
+
+int usb_bulk_read(usb_dev_handle *dev, int ep, char *bytes, int size,
+	int timeout)
+{
+  /* Ensure the endpoint address is correct */
+  ep |= USB_ENDPOINT_IN;
+  return usb_urb_transfer(dev, ep, USB_URB_TYPE_BULK, bytes, size,
+		timeout);
+}
+
+/*
+ * FIXME: Packetize large buffers here. 2.4 HCDs (atleast, haven't checked
+ * 2.5 HCDs yet) don't handle multi-packet Interrupt transfers. So we need
+ * to lookup the endpoint packet size and packetize appropriately here.
+ */
+int usb_interrupt_write(usb_dev_handle *dev, int ep, const char *bytes, int size,
+	int timeout)
+{
+  /* Ensure the endpoint address is correct */
+  return usb_urb_transfer(dev, ep, USB_URB_TYPE_INTERRUPT, (char *)bytes, size,
+		timeout);
+}
+
+int usb_interrupt_read(usb_dev_handle *dev, int ep, char *bytes, int size,
+	int timeout)
+{
+  /* Ensure the endpoint address is correct */
+  ep |= USB_ENDPOINT_IN;
+  return usb_urb_transfer(dev, ep, USB_URB_TYPE_INTERRUPT, bytes, size,
+		timeout);
+}
+
+int usb_os_find_busses(struct usb_bus **busses)
+{
+  struct usb_bus *fbus = NULL;
+  DIR *dir;
+  struct dirent *entry;
+
+  dir = opendir(usb_path);
+  if (!dir)
+    USB_ERROR_STR(-errno, "couldn't opendir(%s): %s", usb_path,
+	strerror(errno));
+
+  while ((entry = readdir(dir)) != NULL) {
+    struct usb_bus *bus;
+
+    /* Skip anything starting with a . */
+    if (entry->d_name[0] == '.')
+      continue;
+
+    if (!strchr("0123456789", entry->d_name[strlen(entry->d_name) - 1])) {
+      if (usb_debug >= 2)
+        fprintf(stderr, "usb_os_find_busses: Skipping non bus directory %s\n",
+		entry->d_name);
+      continue;
+    }
+
+    bus = malloc(sizeof(*bus));
+    if (!bus)
+      USB_ERROR(-ENOMEM);
+
+    memset((void *)bus, 0, sizeof(*bus));
+
+    strncpy(bus->dirname, entry->d_name, sizeof(bus->dirname) - 1);
+    bus->dirname[sizeof(bus->dirname) - 1] = 0;
+    bus->location = atoi(bus->dirname);
+
+    LIST_ADD(fbus, bus);
+
+    if (usb_debug >= 2)
+       fprintf(stderr, "usb_os_find_busses: Found %s\n", bus->dirname);
+  }
+
+  closedir(dir);
+
+  *busses = fbus;
+
+  return 0;
+}
+
+int usb_os_find_devices(struct usb_bus *bus, struct usb_device **devices)
+{
+  struct usb_device *fdev = NULL;
+  DIR *dir;
+  struct dirent *entry;
+  char dirpath[PATH_MAX + 1];
+
+  snprintf(dirpath, PATH_MAX, "%s/%s", usb_path, bus->dirname);
+
+  dir = opendir(dirpath);
+  if (!dir)
+    USB_ERROR_STR(-errno, "couldn't opendir(%s): %s", dirpath,
+	strerror(errno));
+
+  while ((entry = readdir(dir)) != NULL) {
+    unsigned char device_desc[DEVICE_DESC_LENGTH];
+    char filename[PATH_MAX + 1];
+    struct usb_device *dev;
+    struct usb_connectinfo connectinfo;
+    int i, fd, ret;
+
+    /* Skip anything starting with a . */
+    if (entry->d_name[0] == '.')
+      continue;
+
+    dev = malloc(sizeof(*dev));
+    if (!dev)
+      USB_ERROR(-ENOMEM);
+
+    memset((void *)dev, 0, sizeof(*dev));
+
+    dev->bus = bus;
+
+    strncpy(dev->filename, entry->d_name, sizeof(dev->filename) - 1);
+    dev->filename[sizeof(dev->filename) - 1] = 0;
+
+    snprintf(filename, sizeof(filename) - 1, "%s/%s", dirpath, entry->d_name);
+    fd = open(filename, O_RDWR);
+    if (fd < 0) {
+      fd = open(filename, O_RDONLY);
+      if (fd < 0) {
+        if (usb_debug >= 2)
+          fprintf(stderr, "usb_os_find_devices: Couldn't open %s\n",
+                  filename);
+
+        free(dev);
+        continue;
+      }
+    }
+
+    /* Get the device number */
+    ret = ioctl(fd, IOCTL_USB_CONNECTINFO, &connectinfo);
+    if (ret < 0) {
+      if (usb_debug)
+        fprintf(stderr, "usb_os_find_devices: couldn't get connect info\n");
+    } else
+      dev->devnum = connectinfo.devnum;
+
+    ret = read(fd, (void *)device_desc, DEVICE_DESC_LENGTH);
+    if (ret < 0) {
+      if (usb_debug)
+        fprintf(stderr, "usb_os_find_devices: Couldn't read descriptor\n");
+
+      free(dev);
+
+      goto err;
+    }
+
+    /*
+     * Linux kernel converts the words in this descriptor to CPU endian, so
+     * we use the undocumented W character for usb_parse_descriptor() that
+     * doesn't convert endianess when parsing the descriptor
+     */
+    usb_parse_descriptor(device_desc, "bbWbbbbWWWbbbb", &dev->descriptor);
+
+    LIST_ADD(fdev, dev);
+
+    if (usb_debug >= 2)
+      fprintf(stderr, "usb_os_find_devices: Found %s on %s\n",
+		dev->filename, bus->dirname);
+
+    /* Now try to fetch the rest of the descriptors */
+    if (dev->descriptor.bNumConfigurations > USB_MAXCONFIG)
+      /* Silent since we'll try again later */
+      goto err;
+
+    if (dev->descriptor.bNumConfigurations < 1)
+      /* Silent since we'll try again later */
+      goto err;
+
+    dev->config = (struct usb_config_descriptor *)malloc(dev->descriptor.bNumConfigurations * sizeof(struct usb_config_descriptor));
+    if (!dev->config)
+      /* Silent since we'll try again later */
+      goto err;
+
+    memset(dev->config, 0, dev->descriptor.bNumConfigurations *
+          sizeof(struct usb_config_descriptor));
+
+    for (i = 0; i < dev->descriptor.bNumConfigurations; i++) {
+      unsigned char buffer[8], *bigbuffer;
+      struct usb_config_descriptor config;
+
+      /* Get the first 8 bytes so we can figure out what the total length is */
+      ret = read(fd, (void *)buffer, 8);
+      if (ret < 8) {
+        if (usb_debug >= 1) {
+          if (ret < 0)
+            fprintf(stderr, "Unable to get descriptor (%d)\n", ret);
+          else
+            fprintf(stderr, "Config descriptor too short (expected %d, got %d)\n", 8, ret);
+        }
+
+        goto err;
+      }
+
+      usb_parse_descriptor(buffer, "bbw", &config);
+
+      bigbuffer = malloc(config.wTotalLength);
+      if (!bigbuffer) {
+        if (usb_debug >= 1)
+          fprintf(stderr, "Unable to allocate memory for descriptors\n");
+        goto err;
+      }
+
+      /* Read the rest of the config descriptor */
+      memcpy(bigbuffer, buffer, 8);
+
+      ret = read(fd, (void *)(bigbuffer + 8), config.wTotalLength - 8);
+      if (ret < config.wTotalLength - 8) {
+        if (usb_debug >= 1) {
+          if (ret < 0)
+            fprintf(stderr, "Unable to get descriptor (%d)\n", ret);
+          else
+            fprintf(stderr, "Config descriptor too short (expected %d, got %d)\n", config.wTotalLength, ret);
+        }
+
+        free(bigbuffer);
+        goto err;
+      }
+
+      ret = usb_parse_configuration(&dev->config[i], bigbuffer);
+      if (usb_debug >= 2) {
+        if (ret > 0)
+          fprintf(stderr, "Descriptor data still left\n");
+        else if (ret < 0)
+          fprintf(stderr, "Unable to parse descriptors\n");
+      }
+
+      free(bigbuffer);
+    }
+
+err:
+    close(fd);
+  }
+
+  closedir(dir);
+
+  *devices = fdev;
+
+  return 0;
+}
+
+int usb_os_determine_children(struct usb_bus *bus)
+{
+  struct usb_device *dev, *devices[256];
+  struct usb_ioctl command;
+  int ret, i, i1;
+
+  /* Create a list of devices first */
+  memset(devices, 0, sizeof(devices));
+  for (dev = bus->devices; dev; dev = dev->next)
+    if (dev->devnum)
+      devices[dev->devnum] = dev;
+
+  /* Now fetch the children for each device */
+  for (dev = bus->devices; dev; dev = dev->next) {
+    struct usb_hub_portinfo portinfo;
+    int fd;
+
+    fd = device_open(dev);
+    if (fd < 0)
+      continue;
+
+    /* Query the hub driver for the children of this device */
+    if (dev->config && dev->config->interface && dev->config->interface->altsetting)
+      command.ifno = dev->config->interface->altsetting->bInterfaceNumber;
+    else
+      command.ifno = 0;
+    command.ioctl_code = IOCTL_USB_HUB_PORTINFO;
+    command.data = &portinfo;
+    ret = ioctl(fd, IOCTL_USB_IOCTL, &command);
+    if (ret < 0) {
+      /* errno == ENOSYS means the device probably wasn't a hub */
+      if (errno != ENOSYS && usb_debug > 1)
+        fprintf(stderr, "error obtaining child information: %s\n",
+		strerror(errno));
+
+      close(fd);
+      continue;
+    }
+
+    dev->num_children = 0;
+    for (i = 0; i < portinfo.numports; i++)
+      if (portinfo.port[i])
+        dev->num_children++;
+
+    /* Free any old children first */
+    free(dev->children);
+
+    dev->children = malloc(sizeof(struct usb_device *) * dev->num_children);
+    if (!dev->children) {
+      if (usb_debug > 1)
+        fprintf(stderr, "error allocating %zu bytes memory for dev->children\n",
+                sizeof(struct usb_device *) * dev->num_children);
+
+      dev->num_children = 0;
+      close(fd);
+      continue;
+    }
+
+    for (i = 0, i1 = 0; i < portinfo.numports; i++) {
+      if (!portinfo.port[i])
+        continue;
+
+      dev->children[i1++] = devices[portinfo.port[i]];
+
+      devices[portinfo.port[i]] = NULL;
+    }
+
+    close(fd);
+  }
+
+  /*
+   * There should be one device left in the devices list and that should be
+   * the root device
+   */
+  for (i = 0; i < sizeof(devices) / sizeof(devices[0]); i++) {
+    if (devices[i])
+      bus->root_dev = devices[i];
+  }
+
+  return 0;
+}
+
+static int check_usb_vfs(const char *dirname)
+{
+  DIR *dir;
+  struct dirent *entry;
+  int found = 0;
+
+  dir = opendir(dirname);
+  if (!dir)
+    return 0;
+
+  while ((entry = readdir(dir)) != NULL) {
+    /* Skip anything starting with a . */
+    if (entry->d_name[0] == '.')
+      continue;
+
+    /* We assume if we find any files that it must be the right place */
+    found = 1;
+    break;
+  }
+
+  closedir(dir);
+
+  return found;
+}
+
+void usb_os_init(void)
+{
+  /* Find the path to the virtual filesystem */
+  if (getenv("USB_DEVFS_PATH")) {
+    if (check_usb_vfs(getenv("USB_DEVFS_PATH"))) {
+      strncpy(usb_path, getenv("USB_DEVFS_PATH"), sizeof(usb_path) - 1);
+      usb_path[sizeof(usb_path) - 1] = 0;
+    } else if (usb_debug)
+      fprintf(stderr, "usb_os_init: couldn't find USB VFS in USB_DEVFS_PATH\n");
+  }
+
+  if (!usb_path[0]) {
+    if (check_usb_vfs("/dev/bus/usb")) {
+      strncpy(usb_path, "/dev/bus/usb", sizeof(usb_path) - 1);
+      usb_path[sizeof(usb_path) - 1] = 0;
+    } else if (check_usb_vfs("/proc/bus/usb")) {
+      strncpy(usb_path, "/proc/bus/usb", sizeof(usb_path) - 1);
+      usb_path[sizeof(usb_path) - 1] = 0;
+    } else
+      usb_path[0] = 0;	/* No path, no USB support */
+  }
+
+  if (usb_debug) {
+    if (usb_path[0])
+      fprintf(stderr, "usb_os_init: Found USB VFS at %s\n", usb_path);
+    else
+      fprintf(stderr, "usb_os_init: No USB VFS found, is it mounted?\n");
+  }
+}
+
+int usb_resetep(usb_dev_handle *dev, unsigned int ep)
+{
+  int ret;
+
+  ret = ioctl(dev->fd, IOCTL_USB_RESETEP, &ep);
+  if (ret)
+    USB_ERROR_STR(-errno, "could not reset ep %d: %s", ep,
+    	strerror(errno));
+
+  return 0;
+}
+
+int usb_clear_halt(usb_dev_handle *dev, unsigned int ep)
+{
+  int ret;
+
+  ret = ioctl(dev->fd, IOCTL_USB_CLEAR_HALT, &ep);
+  if (ret)
+    USB_ERROR_STR(-errno, "could not clear/halt ep %d: %s", ep,
+    	strerror(errno));
+
+  return 0;
+}
+
+int usb_reset(usb_dev_handle *dev)
+{
+  int ret;
+
+  ret = ioctl(dev->fd, IOCTL_USB_RESET, NULL);
+  if (ret)
+     USB_ERROR_STR(-errno, "could not reset: %s", strerror(errno));
+
+  return 0;
+}
+
+int usb_get_driver_np(usb_dev_handle *dev, int interface, char *name,
+	unsigned int namelen)
+{
+  struct usb_getdriver getdrv;
+  int ret;
+
+  getdrv.interface = interface;
+  ret = ioctl(dev->fd, IOCTL_USB_GETDRIVER, &getdrv);
+  if (ret)
+    USB_ERROR_STR(-errno, "could not get bound driver: %s", strerror(errno));
+
+  strncpy(name, getdrv.driver, namelen - 1);
+  name[namelen - 1] = 0;
+
+  return 0;
+}
+
+int usb_detach_kernel_driver_np(usb_dev_handle *dev, int interface)
+{
+  struct usb_ioctl command;
+  int ret;
+
+  command.ifno = interface;
+  command.ioctl_code = IOCTL_USB_DISCONNECT;
+  command.data = NULL;
+
+  ret = ioctl(dev->fd, IOCTL_USB_IOCTL, &command);
+  if (ret)
+    USB_ERROR_STR(-errno, "could not detach kernel driver from interface %d: %s",
+        interface, strerror(errno));
+
+  return 0;
+}
+

=== modified file '.pc/applied-patches'
--- .pc/applied-patches	2014-07-03 20:17:03 +0000
+++ .pc/applied-patches	2015-05-17 16:46:05 +0000
@@ -10,4 +10,5 @@
 09_dummy.diff
 10_hurd.diff
 11_transfer_timeout.diff
+12_hang_after_resume.diff
 91_ac_prog_cxx.diff

=== modified file 'debian/changelog'

=== added file 'debian/patches/12_hang_after_resume.diff'
--- debian/patches/12_hang_after_resume.diff	1970-01-01 00:00:00 +0000
+++ debian/patches/12_hang_after_resume.diff	2015-05-17 16:45:30 +0000
@@ -0,0 +1,47 @@
+Description: Fix occasional hang after suspend/resume.
+ After suspend/resume linux kernel may loose our requests, and 
+ usb_urb_transfer() will wait forever for lost requests to complete. With this
+ patch lost request are properly handled.
+ Also fixes race condition leading to stack corruption.
+Author: Alexander Ponyatykh <lazyra...@gmail.com>
+Bug-Ubuntu: https://bugs.launchpad.net/ubuntu/+source/libusb/+bug/1455924
+Forwarded: no
+Last-Update: 2015-05-17
+===================================================================
+--- a/linux.c
++++ b/linux.c
+@@ -166,7 +166,7 @@
+   int bytesdone = 0, requested;
+   struct timeval tv, tv_ref, tv_now;
+   struct usb_urb *context;
+-  int ret, waiting;
++  int ret, waiting, our_urb_dequeued;  
+ 
+   /*
+    * HACK: The use of urb.usercontext is a hack to get threaded applications
+@@ -277,7 +277,24 @@
+      * then we need to reap it or else the next time we call this function,
+      * we'll get the previous completion and exit early
+      */
+-    ioctl(dev->fd, IOCTL_USB_REAPURB, &context);
++    our_urb_dequeued = 0;
++    do {
++      context = NULL;
++      ret = ioctl(dev->fd, IOCTL_USB_REAPURBNDELAY, &context);
++      if (context) {
++        if (context == &urb) {
++          our_urb_dequeued = 1;
++        } else {
++          /* We need to retry since we got completed URB, but not ours.
++          * Otherwise other thread may write to our already deallocated
++          * urb.usercontext */
++          context->usercontext = URB_USERCONTEXT_COOKIE;
++        }
++      }
++    } while (context && !our_urb_dequeued);
++
++    if (usb_debug > 0 && !our_urb_dequeued && !urb.usercontext)
++      fprintf(stderr, "usb_urb_transfer: kernel may have lost our URB! (or other thread dequeued it)\n");
+ 
+     return rc;
+   }

=== modified file 'debian/patches/series'
--- debian/patches/series	2014-07-03 20:17:03 +0000
+++ debian/patches/series	2015-05-17 16:40:43 +0000
@@ -10,4 +10,5 @@
 09_dummy.diff
 10_hurd.diff
 11_transfer_timeout.diff
+12_hang_after_resume.diff
 91_ac_prog_cxx.diff

=== modified file 'linux.c'
--- linux.c	2014-07-03 20:17:03 +0000
+++ linux.c	2015-05-17 14:25:02 +0000
@@ -166,7 +166,7 @@
   int bytesdone = 0, requested;
   struct timeval tv, tv_ref, tv_now;
   struct usb_urb *context;
-  int ret, waiting;
+  int ret, waiting, our_urb_dequeued;  
 
   /*
    * HACK: The use of urb.usercontext is a hack to get threaded applications
@@ -277,7 +277,24 @@
      * then we need to reap it or else the next time we call this function,
      * we'll get the previous completion and exit early
      */
-    ioctl(dev->fd, IOCTL_USB_REAPURB, &context);
+    our_urb_dequeued = 0;
+    do {
+      context = NULL;
+      ret = ioctl(dev->fd, IOCTL_USB_REAPURBNDELAY, &context);
+      if (context) {
+        if (context == &urb) {
+          our_urb_dequeued = 1;
+        } else {
+          /* We need to retry since we got completed URB, but not ours.
+          * Otherwise other thread may write to our already deallocated
+          * urb.usercontext */
+          context->usercontext = URB_USERCONTEXT_COOKIE;
+        }
+      }
+    } while (context && !our_urb_dequeued);
+
+    if (usb_debug > 0 && !our_urb_dequeued && !urb.usercontext)
+      fprintf(stderr, "usb_urb_transfer: kernel may have lost our URB! (or other thread dequeued it)\n");
 
     return rc;
   }

Reply via email to