Merge tag 'v3.10.85' into update
[GitHub/mt8127/android_kernel_alcatel_ttab.git] / drivers / usb / core / devio.c
index 6b914e2f6ac4f3ed51b6639d9f9a36194adb3ac7..5e3f8c7e59269295cfe21aa83f889eb7284302d3 100644 (file)
@@ -513,7 +513,7 @@ static void async_completed(struct urb *urb)
        snoop(&urb->dev->dev, "urb complete\n");
        snoop_urb(urb->dev, as->userurb, urb->pipe, urb->actual_length,
                        as->status, COMPLETE, NULL, 0);
-       if ((urb->transfer_flags & URB_DIR_MASK) == USB_DIR_IN)
+       if ((urb->transfer_flags & URB_DIR_MASK) == URB_DIR_IN)
                snoop_urb_data(urb, urb->actual_length);
 
        if (as->status < 0 && as->bulk_addr && as->status != -ECONNRESET &&
@@ -1598,7 +1598,7 @@ static struct async *reap_as(struct dev_state *ps)
        for (;;) {
                __set_current_state(TASK_INTERRUPTIBLE);
                as = async_getcompleted(ps);
-               if (as)
+               if (as || !connected(ps))
                        break;
                if (signal_pending(current))
                        break;
@@ -1621,7 +1621,7 @@ static int proc_reapurb(struct dev_state *ps, void __user *arg)
        }
        if (signal_pending(current))
                return -EINTR;
-       return -EIO;
+       return -ENODEV;
 }
 
 static int proc_reapurbnonblock(struct dev_state *ps, void __user *arg)
@@ -1630,10 +1630,11 @@ static int proc_reapurbnonblock(struct dev_state *ps, void __user *arg)
        struct async *as;
 
        as = async_getcompleted(ps);
-       retval = -EAGAIN;
        if (as) {
                retval = processcompl(as, (void __user * __user *)arg);
                free_async(as);
+       } else {
+               retval = (connected(ps) ? -EAGAIN : -ENODEV);
        }
        return retval;
 }
@@ -1763,7 +1764,7 @@ static int proc_reapurb_compat(struct dev_state *ps, void __user *arg)
        }
        if (signal_pending(current))
                return -EINTR;
-       return -EIO;
+       return -ENODEV;
 }
 
 static int proc_reapurbnonblock_compat(struct dev_state *ps, void __user *arg)
@@ -1771,11 +1772,12 @@ static int proc_reapurbnonblock_compat(struct dev_state *ps, void __user *arg)
        int retval;
        struct async *as;
 
-       retval = -EAGAIN;
        as = async_getcompleted(ps);
        if (as) {
                retval = processcompl_compat(as, (void __user * __user *)arg);
                free_async(as);
+       } else {
+               retval = (connected(ps) ? -EAGAIN : -ENODEV);
        }
        return retval;
 }
@@ -1946,7 +1948,8 @@ static int proc_get_capabilities(struct dev_state *ps, void __user *arg)
 {
        __u32 caps;
 
-       caps = USBDEVFS_CAP_ZERO_PACKET | USBDEVFS_CAP_NO_PACKET_SIZE_LIM;
+       caps = USBDEVFS_CAP_ZERO_PACKET | USBDEVFS_CAP_NO_PACKET_SIZE_LIM |
+                       USBDEVFS_CAP_REAP_AFTER_DISCONNECT;
        if (!ps->dev->bus->no_stop_on_short)
                caps |= USBDEVFS_CAP_BULK_CONTINUATION;
        if (ps->dev->bus->sg_tablesize)
@@ -2007,6 +2010,32 @@ static long usbdev_do_ioctl(struct file *file, unsigned int cmd,
                return -EPERM;
 
        usb_lock_device(dev);
+
+       /* Reap operations are allowed even after disconnection */
+       switch (cmd) {
+       case USBDEVFS_REAPURB:
+               snoop(&dev->dev, "%s: REAPURB\n", __func__);
+               ret = proc_reapurb(ps, p);
+               goto done;
+
+       case USBDEVFS_REAPURBNDELAY:
+               snoop(&dev->dev, "%s: REAPURBNDELAY\n", __func__);
+               ret = proc_reapurbnonblock(ps, p);
+               goto done;
+
+#ifdef CONFIG_COMPAT
+       case USBDEVFS_REAPURB32:
+               snoop(&dev->dev, "%s: REAPURB32\n", __func__);
+               ret = proc_reapurb_compat(ps, p);
+               goto done;
+
+       case USBDEVFS_REAPURBNDELAY32:
+               snoop(&dev->dev, "%s: REAPURBNDELAY32\n", __func__);
+               ret = proc_reapurbnonblock_compat(ps, p);
+               goto done;
+#endif
+       }
+
        if (!connected(ps)) {
                usb_unlock_device(dev);
                return -ENODEV;
@@ -2100,16 +2129,6 @@ static long usbdev_do_ioctl(struct file *file, unsigned int cmd,
                        inode->i_mtime = CURRENT_TIME;
                break;
 
-       case USBDEVFS_REAPURB32:
-               snoop(&dev->dev, "%s: REAPURB32\n", __func__);
-               ret = proc_reapurb_compat(ps, p);
-               break;
-
-       case USBDEVFS_REAPURBNDELAY32:
-               snoop(&dev->dev, "%s: REAPURBNDELAY32\n", __func__);
-               ret = proc_reapurbnonblock_compat(ps, p);
-               break;
-
        case USBDEVFS_IOCTL32:
                snoop(&dev->dev, "%s: IOCTL32\n", __func__);
                ret = proc_ioctl_compat(ps, ptr_to_compat(p));
@@ -2121,16 +2140,6 @@ static long usbdev_do_ioctl(struct file *file, unsigned int cmd,
                ret = proc_unlinkurb(ps, p);
                break;
 
-       case USBDEVFS_REAPURB:
-               snoop(&dev->dev, "%s: REAPURB\n", __func__);
-               ret = proc_reapurb(ps, p);
-               break;
-
-       case USBDEVFS_REAPURBNDELAY:
-               snoop(&dev->dev, "%s: REAPURBNDELAY\n", __func__);
-               ret = proc_reapurbnonblock(ps, p);
-               break;
-
        case USBDEVFS_DISCSIGNAL:
                snoop(&dev->dev, "%s: DISCSIGNAL\n", __func__);
                ret = proc_disconnectsignal(ps, p);
@@ -2167,6 +2176,8 @@ static long usbdev_do_ioctl(struct file *file, unsigned int cmd,
                ret = proc_disconnect_claim(ps, p);
                break;
        }
+
+ done:
        usb_unlock_device(dev);
        if (ret >= 0)
                inode->i_atime = CURRENT_TIME;