USB: pid_ns: ensure pid is not freed during kill_pid_info_as_uid
[linux-2.6.git] / drivers / usb / core / devio.c
index 6585f52..0ca54e2 100644 (file)
 #include <linux/fs.h>
 #include <linux/mm.h>
 #include <linux/slab.h>
-#include <linux/smp_lock.h>
 #include <linux/signal.h>
 #include <linux/poll.h>
 #include <linux/module.h>
 #include <linux/usb.h>
 #include <linux/usbdevice_fs.h>
+#include <linux/usb/hcd.h>     /* for usbcore internals */
 #include <linux/cdev.h>
 #include <linux/notifier.h>
 #include <linux/security.h>
@@ -50,7 +50,6 @@
 #include <asm/byteorder.h>
 #include <linux/moduleparam.h>
 
-#include "hcd.h"       /* for usbcore internals */
 #include "usb.h"
 
 #define USB_MAXBUS                     64
@@ -73,6 +72,7 @@ struct dev_state {
        void __user *disccontext;
        unsigned long ifclaimed;
        u32 secid;
+       u32 disabled_bulk_eps;
 };
 
 struct async {
@@ -87,6 +87,8 @@ struct async {
        struct urb *urb;
        int status;
        u32 secid;
+       u8 bulk_addr;
+       u8 bulk_status;
 };
 
 static int usbfs_snoop;
@@ -99,12 +101,16 @@ MODULE_PARM_DESC(usbfs_snoop, "true to log all usbfs traffic");
                        dev_info(dev , format , ## arg);        \
        } while (0)
 
-#define USB_DEVICE_DEV         MKDEV(USB_DEVICE_MAJOR, 0)
+enum snoop_when {
+       SUBMIT, COMPLETE
+};
 
+#define USB_DEVICE_DEV         MKDEV(USB_DEVICE_MAJOR, 0)
 
 #define        MAX_USBFS_BUFFER_SIZE   16384
 
-static inline int connected(struct dev_state *ps)
+
+static int connected(struct dev_state *ps)
 {
        return (!list_empty(&ps->list) &&
                        ps->dev->state != USB_STATE_NOTATTACHED);
@@ -114,7 +120,7 @@ static loff_t usbdev_lseek(struct file *file, loff_t offset, int orig)
 {
        loff_t ret;
 
-       lock_kernel();
+       mutex_lock(&file->f_dentry->d_inode->i_mutex);
 
        switch (orig) {
        case 0:
@@ -130,7 +136,7 @@ static loff_t usbdev_lseek(struct file *file, loff_t offset, int orig)
                ret = -EINVAL;
        }
 
-       unlock_kernel();
+       mutex_unlock(&file->f_dentry->d_inode->i_mutex);
        return ret;
 }
 
@@ -248,7 +254,7 @@ static void free_async(struct async *as)
        kfree(as);
 }
 
-static inline void async_newpending(struct async *as)
+static void async_newpending(struct async *as)
 {
        struct dev_state *ps = as->ps;
        unsigned long flags;
@@ -258,7 +264,7 @@ static inline void async_newpending(struct async *as)
        spin_unlock_irqrestore(&ps->lock, flags);
 }
 
-static inline void async_removepending(struct async *as)
+static void async_removepending(struct async *as)
 {
        struct dev_state *ps = as->ps;
        unsigned long flags;
@@ -268,7 +274,7 @@ static inline void async_removepending(struct async *as)
        spin_unlock_irqrestore(&ps->lock, flags);
 }
 
-static inline struct async *async_getcompleted(struct dev_state *ps)
+static struct async *async_getcompleted(struct dev_state *ps)
 {
        unsigned long flags;
        struct async *as = NULL;
@@ -283,7 +289,7 @@ static inline struct async *async_getcompleted(struct dev_state *ps)
        return as;
 }
 
-static inline struct async *async_getpending(struct dev_state *ps,
+static struct async *async_getpending(struct dev_state *ps,
                                             void __user *userurb)
 {
        unsigned long flags;
@@ -300,24 +306,85 @@ static inline struct async *async_getpending(struct dev_state *ps,
        return NULL;
 }
 
-static void snoop_urb(struct urb *urb, void __user *userurb)
+static void snoop_urb(struct usb_device *udev,
+               void __user *userurb, int pipe, unsigned length,
+               int timeout_or_status, enum snoop_when when,
+               unsigned char *data, unsigned data_len)
 {
-       int j;
-       unsigned char *data = urb->transfer_buffer;
+       static const char *types[] = {"isoc", "int", "ctrl", "bulk"};
+       static const char *dirs[] = {"out", "in"};
+       int ep;
+       const char *t, *d;
 
        if (!usbfs_snoop)
                return;
 
-       dev_info(&urb->dev->dev, "direction=%s\n",
-                       usb_urb_dir_in(urb) ? "IN" : "OUT");
-       dev_info(&urb->dev->dev, "userurb=%p\n", userurb);
-       dev_info(&urb->dev->dev, "transfer_buffer_length=%d\n",
-                urb->transfer_buffer_length);
-       dev_info(&urb->dev->dev, "actual_length=%d\n", urb->actual_length);
-       dev_info(&urb->dev->dev, "data: ");
-       for (j = 0; j < urb->transfer_buffer_length; ++j)
-               printk("%02x ", data[j]);
-       printk("\n");
+       ep = usb_pipeendpoint(pipe);
+       t = types[usb_pipetype(pipe)];
+       d = dirs[!!usb_pipein(pipe)];
+
+       if (userurb) {          /* Async */
+               if (when == SUBMIT)
+                       dev_info(&udev->dev, "userurb %p, ep%d %s-%s, "
+                                       "length %u\n",
+                                       userurb, ep, t, d, length);
+               else
+                       dev_info(&udev->dev, "userurb %p, ep%d %s-%s, "
+                                       "actual_length %u status %d\n",
+                                       userurb, ep, t, d, length,
+                                       timeout_or_status);
+       } else {
+               if (when == SUBMIT)
+                       dev_info(&udev->dev, "ep%d %s-%s, length %u, "
+                                       "timeout %d\n",
+                                       ep, t, d, length, timeout_or_status);
+               else
+                       dev_info(&udev->dev, "ep%d %s-%s, actual_length %u, "
+                                       "status %d\n",
+                                       ep, t, d, length, timeout_or_status);
+       }
+
+       if (data && data_len > 0) {
+               print_hex_dump(KERN_DEBUG, "data: ", DUMP_PREFIX_NONE, 32, 1,
+                       data, data_len, 1);
+       }
+}
+
+#define AS_CONTINUATION        1
+#define AS_UNLINK      2
+
+static void cancel_bulk_urbs(struct dev_state *ps, unsigned bulk_addr)
+__releases(ps->lock)
+__acquires(ps->lock)
+{
+       struct async *as;
+
+       /* Mark all the pending URBs that match bulk_addr, up to but not
+        * including the first one without AS_CONTINUATION.  If such an
+        * URB is encountered then a new transfer has already started so
+        * the endpoint doesn't need to be disabled; otherwise it does.
+        */
+       list_for_each_entry(as, &ps->async_pending, asynclist) {
+               if (as->bulk_addr == bulk_addr) {
+                       if (as->bulk_status != AS_CONTINUATION)
+                               goto rescan;
+                       as->bulk_status = AS_UNLINK;
+                       as->bulk_addr = 0;
+               }
+       }
+       ps->disabled_bulk_eps |= (1 << bulk_addr);
+
+       /* Now carefully unlink all the marked pending URBs */
+ rescan:
+       list_for_each_entry(as, &ps->async_pending, asynclist) {
+               if (as->bulk_status == AS_UNLINK) {
+                       as->bulk_status = 0;            /* Only once */
+                       spin_unlock(&ps->lock);         /* Allow completions */
+                       usb_unlink_urb(as->urb);
+                       spin_lock(&ps->lock);
+                       goto rescan;
+               }
+       }
 }
 
 static void async_completed(struct urb *urb)
@@ -325,21 +392,42 @@ static void async_completed(struct urb *urb)
        struct async *as = urb->context;
        struct dev_state *ps = as->ps;
        struct siginfo sinfo;
+       struct pid *pid = NULL;
+       uid_t uid = 0;
+       uid_t euid = 0;
+       u32 secid = 0;
+       int signr;
 
        spin_lock(&ps->lock);
        list_move_tail(&as->asynclist, &ps->async_completed);
-       spin_unlock(&ps->lock);
        as->status = urb->status;
-       if (as->signr) {
+       signr = as->signr;
+       if (signr) {
                sinfo.si_signo = as->signr;
                sinfo.si_errno = as->status;
                sinfo.si_code = SI_ASYNCIO;
                sinfo.si_addr = as->userurb;
-               kill_pid_info_as_uid(as->signr, &sinfo, as->pid, as->uid,
-                                     as->euid, as->secid);
+               pid = get_pid(as->pid);
+               uid = as->uid;
+               euid = as->euid;
+               secid = as->secid;
        }
        snoop(&urb->dev->dev, "urb complete\n");
-       snoop_urb(urb, as->userurb);
+       snoop_urb(urb->dev, as->userurb, urb->pipe, urb->actual_length,
+                       as->status, COMPLETE,
+                       ((urb->transfer_flags & URB_DIR_MASK) == USB_DIR_OUT) ?
+                               NULL : urb->transfer_buffer, urb->actual_length);
+       if (as->status < 0 && as->bulk_addr && as->status != -ECONNRESET &&
+                       as->status != -ENOENT)
+               cancel_bulk_urbs(ps, as->bulk_addr);
+       spin_unlock(&ps->lock);
+
+       if (signr) {
+               kill_pid_info_as_uid(sinfo.si_signo, &sinfo, pid, uid,
+                                     euid, secid);
+               put_pid(pid);
+       }
+
        wake_up(&ps->wait);
 }
 
@@ -376,7 +464,7 @@ static void destroy_async_on_interface(struct dev_state *ps,
        destroy_async(ps, &hitlist);
 }
 
-static inline void destroy_all_async(struct dev_state *ps)
+static void destroy_all_async(struct dev_state *ps)
 {
        destroy_async(ps, &ps->async_pending);
 }
@@ -521,16 +609,31 @@ static int findintfep(struct usb_device *dev, unsigned int ep)
 }
 
 static int check_ctrlrecip(struct dev_state *ps, unsigned int requesttype,
-                          unsigned int index)
+                          unsigned int request, unsigned int index)
 {
        int ret = 0;
+       struct usb_host_interface *alt_setting;
 
-       if (ps->dev->state != USB_STATE_ADDRESS
+       if (ps->dev->state != USB_STATE_UNAUTHENTICATED
+        && ps->dev->state != USB_STATE_ADDRESS
         && ps->dev->state != USB_STATE_CONFIGURED)
                return -EHOSTUNREACH;
        if (USB_TYPE_VENDOR == (USB_TYPE_MASK & requesttype))
                return 0;
 
+       /*
+        * check for the special corner case 'get_device_id' in the printer
+        * class specification, where wIndex is (interface << 8 | altsetting)
+        * instead of just interface
+        */
+       if (requesttype == 0xa1 && request == 0) {
+               alt_setting = usb_find_alt_setting(ps->dev->actconfig,
+                                                  index >> 8, index & 0xff);
+               if (alt_setting
+                && alt_setting->desc.bInterfaceClass == USB_CLASS_PRINTER)
+                       index >>= 8;
+       }
+
        index &= 0xff;
        switch (requesttype & USB_RECIP_MASK) {
        case USB_RECIP_ENDPOINT:
@@ -572,20 +675,20 @@ static int usbdev_open(struct inode *inode, struct file *file)
        const struct cred *cred = current_cred();
        int ret;
 
-       lock_kernel();
-       /* Protect against simultaneous removal or release */
-       mutex_lock(&usbfs_mutex);
-
        ret = -ENOMEM;
        ps = kmalloc(sizeof(struct dev_state), GFP_KERNEL);
        if (!ps)
-               goto out;
+               goto out_free_ps;
 
-       ret = -ENOENT;
+       ret = -ENODEV;
+
+       /* Protect against simultaneous removal or release */
+       mutex_lock(&usbfs_mutex);
 
        /* usbdev device-node */
        if (imajor(inode) == USB_DEVICE_MAJOR)
                dev = usbdev_lookup_by_devt(inode->i_rdev);
+
 #ifdef CONFIG_USB_DEVICEFS
        /* procfs file */
        if (!dev) {
@@ -597,13 +700,19 @@ static int usbdev_open(struct inode *inode, struct file *file)
                        dev = NULL;
        }
 #endif
-       if (!dev || dev->state == USB_STATE_NOTATTACHED)
-               goto out;
+       mutex_unlock(&usbfs_mutex);
+
+       if (!dev)
+               goto out_free_ps;
+
+       usb_lock_device(dev);
+       if (dev->state == USB_STATE_NOTATTACHED)
+               goto out_unlock_device;
+
        ret = usb_autoresume_device(dev);
        if (ret)
-               goto out;
+               goto out_unlock_device;
 
-       ret = 0;
        ps->dev = dev;
        ps->file = file;
        spin_lock_init(&ps->lock);
@@ -621,15 +730,16 @@ static int usbdev_open(struct inode *inode, struct file *file)
        smp_wmb();
        list_add_tail(&ps->list, &dev->filelist);
        file->private_data = ps;
+       usb_unlock_device(dev);
        snoop(&dev->dev, "opened by process %d: %s\n", task_pid_nr(current),
                        current->comm);
- out:
-       if (ret) {
-               kfree(ps);
-               usb_put_dev(dev);
-       }
-       mutex_unlock(&usbfs_mutex);
-       unlock_kernel();
+       return ret;
+
+ out_unlock_device:
+       usb_unlock_device(dev);
+       usb_put_dev(dev);
+ out_free_ps:
+       kfree(ps);
        return ret;
 }
 
@@ -641,11 +751,9 @@ static int usbdev_release(struct inode *inode, struct file *file)
        struct async *as;
 
        usb_lock_device(dev);
+       usb_hub_release_all_ports(dev, ps);
 
-       /* Protect against simultaneous open */
-       mutex_lock(&usbfs_mutex);
        list_del_init(&ps->list);
-       mutex_unlock(&usbfs_mutex);
 
        for (ifnum = 0; ps->ifclaimed && ifnum < 8*sizeof(ps->ifclaimed);
                        ifnum++) {
@@ -674,11 +782,12 @@ static int proc_control(struct dev_state *ps, void __user *arg)
        unsigned int tmo;
        unsigned char *tbuf;
        unsigned wLength;
-       int i, j, ret;
+       int i, pipe, ret;
 
        if (copy_from_user(&ctrl, arg, sizeof(ctrl)))
                return -EFAULT;
-       ret = check_ctrlrecip(ps, ctrl.bRequestType, ctrl.wIndex);
+       ret = check_ctrlrecip(ps, ctrl.bRequestType, ctrl.bRequest,
+                             ctrl.wIndex);
        if (ret)
                return ret;
        wLength = ctrl.wLength;         /* To suppress 64k PAGE_SIZE warning */
@@ -688,30 +797,30 @@ static int proc_control(struct dev_state *ps, void __user *arg)
        if (!tbuf)
                return -ENOMEM;
        tmo = ctrl.timeout;
+       snoop(&dev->dev, "control urb: bRequestType=%02x "
+               "bRequest=%02x wValue=%04x "
+               "wIndex=%04x wLength=%04x\n",
+               ctrl.bRequestType, ctrl.bRequest,
+               __le16_to_cpup(&ctrl.wValue),
+               __le16_to_cpup(&ctrl.wIndex),
+               __le16_to_cpup(&ctrl.wLength));
        if (ctrl.bRequestType & 0x80) {
                if (ctrl.wLength && !access_ok(VERIFY_WRITE, ctrl.data,
                                               ctrl.wLength)) {
                        free_page((unsigned long)tbuf);
                        return -EINVAL;
                }
-               snoop(&dev->dev, "control read: bRequest=%02x "
-                               "bRrequestType=%02x wValue=%04x "
-                               "wIndex=%04x wLength=%04x\n",
-                       ctrl.bRequest, ctrl.bRequestType, ctrl.wValue,
-                               ctrl.wIndex, ctrl.wLength);
+               pipe = usb_rcvctrlpipe(dev, 0);
+               snoop_urb(dev, NULL, pipe, ctrl.wLength, tmo, SUBMIT, NULL, 0);
 
                usb_unlock_device(dev);
-               i = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), ctrl.bRequest,
+               i = usb_control_msg(dev, pipe, ctrl.bRequest,
                                    ctrl.bRequestType, ctrl.wValue, ctrl.wIndex,
                                    tbuf, ctrl.wLength, tmo);
                usb_lock_device(dev);
+               snoop_urb(dev, NULL, pipe, max(i, 0), min(i, 0), COMPLETE,
+                         tbuf, max(i, 0));
                if ((i > 0) && ctrl.wLength) {
-                       if (usbfs_snoop) {
-                               dev_info(&dev->dev, "control read: data ");
-                               for (j = 0; j < i; ++j)
-                                       printk("%02x ", (u8)(tbuf)[j]);
-                               printk("\n");
-                       }
                        if (copy_to_user(ctrl.data, tbuf, i)) {
                                free_page((unsigned long)tbuf);
                                return -EFAULT;
@@ -724,22 +833,16 @@ static int proc_control(struct dev_state *ps, void __user *arg)
                                return -EFAULT;
                        }
                }
-               snoop(&dev->dev, "control write: bRequest=%02x "
-                               "bRrequestType=%02x wValue=%04x "
-                               "wIndex=%04x wLength=%04x\n",
-                       ctrl.bRequest, ctrl.bRequestType, ctrl.wValue,
-                               ctrl.wIndex, ctrl.wLength);
-               if (usbfs_snoop) {
-                       dev_info(&dev->dev, "control write: data: ");
-                       for (j = 0; j < ctrl.wLength; ++j)
-                               printk("%02x ", (unsigned char)(tbuf)[j]);
-                       printk("\n");
-               }
+               pipe = usb_sndctrlpipe(dev, 0);
+               snoop_urb(dev, NULL, pipe, ctrl.wLength, tmo, SUBMIT,
+                       tbuf, ctrl.wLength);
+
                usb_unlock_device(dev);
                i = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), ctrl.bRequest,
                                    ctrl.bRequestType, ctrl.wValue, ctrl.wIndex,
                                    tbuf, ctrl.wLength, tmo);
                usb_lock_device(dev);
+               snoop_urb(dev, NULL, pipe, max(i, 0), min(i, 0), COMPLETE, NULL, 0);
        }
        free_page((unsigned long)tbuf);
        if (i < 0 && i != -EPIPE) {
@@ -758,7 +861,7 @@ static int proc_bulk(struct dev_state *ps, void __user *arg)
        unsigned int tmo, len1, pipe;
        int len2;
        unsigned char *tbuf;
-       int i, j, ret;
+       int i, ret;
 
        if (copy_from_user(&bulk, arg, sizeof(bulk)))
                return -EFAULT;
@@ -785,18 +888,14 @@ static int proc_bulk(struct dev_state *ps, void __user *arg)
                        kfree(tbuf);
                        return -EINVAL;
                }
-               snoop(&dev->dev, "bulk read: len=0x%02x timeout=%04d\n",
-                       bulk.len, bulk.timeout);
+               snoop_urb(dev, NULL, pipe, len1, tmo, SUBMIT, NULL, 0);
+
                usb_unlock_device(dev);
                i = usb_bulk_msg(dev, pipe, tbuf, len1, &len2, tmo);
                usb_lock_device(dev);
+               snoop_urb(dev, NULL, pipe, len2, i, COMPLETE, tbuf, len2);
+
                if (!i && len2) {
-                       if (usbfs_snoop) {
-                               dev_info(&dev->dev, "bulk read: data ");
-                               for (j = 0; j < len2; ++j)
-                                       printk("%02x ", (u8)(tbuf)[j]);
-                               printk("\n");
-                       }
                        if (copy_to_user(bulk.data, tbuf, len2)) {
                                kfree(tbuf);
                                return -EFAULT;
@@ -809,17 +908,12 @@ static int proc_bulk(struct dev_state *ps, void __user *arg)
                                return -EFAULT;
                        }
                }
-               snoop(&dev->dev, "bulk write: len=0x%02x timeout=%04d\n",
-                       bulk.len, bulk.timeout);
-               if (usbfs_snoop) {
-                       dev_info(&dev->dev, "bulk write: data: ");
-                       for (j = 0; j < len1; ++j)
-                               printk("%02x ", (unsigned char)(tbuf)[j]);
-                       printk("\n");
-               }
+               snoop_urb(dev, NULL, pipe, len1, tmo, SUBMIT, tbuf, len1);
+
                usb_unlock_device(dev);
                i = usb_bulk_msg(dev, pipe, tbuf, len1, &len2, tmo);
                usb_lock_device(dev);
+               snoop_urb(dev, NULL, pipe, len2, i, COMPLETE, NULL, 0);
        }
        kfree(tbuf);
        if (i < 0)
@@ -840,7 +934,7 @@ static int proc_resetep(struct dev_state *ps, void __user *arg)
        ret = checkintf(ps, ret);
        if (ret)
                return ret;
-       usb_settoggle(ps->dev, ep & 0xf, !(ep & USB_DIR_IN), 0);
+       usb_reset_endpoint(ps->dev, ep);
        return 0;
 }
 
@@ -887,10 +981,11 @@ static int proc_getdriver(struct dev_state *ps, void __user *arg)
 
 static int proc_connectinfo(struct dev_state *ps, void __user *arg)
 {
-       struct usbdevfs_connectinfo ci;
+       struct usbdevfs_connectinfo ci = {
+               .devnum = ps->dev->devnum,
+               .slow = ps->dev->speed == USB_SPEED_LOW
+       };
 
-       ci.devnum = ps->dev->devnum;
-       ci.slow = ps->dev->speed == USB_SPEED_LOW;
        if (copy_to_user(arg, &ci, sizeof(ci)))
                return -EFAULT;
        return 0;
@@ -977,11 +1072,12 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,
 
        if (uurb->flags & ~(USBDEVFS_URB_ISO_ASAP |
                                USBDEVFS_URB_SHORT_NOT_OK |
+                               USBDEVFS_URB_BULK_CONTINUATION |
                                USBDEVFS_URB_NO_FSBR |
                                USBDEVFS_URB_ZERO_PACKET |
                                USBDEVFS_URB_NO_INTERRUPT))
                return -EINVAL;
-       if (!uurb->buffer)
+       if (uurb->buffer_length > 0 && !uurb->buffer)
                return -EINVAL;
        if (!(uurb->type == USBDEVFS_URB_TYPE_CONTROL &&
            (uurb->endpoint & ~USB_ENDPOINT_DIR_MASK) == 0)) {
@@ -1021,7 +1117,7 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,
                        kfree(dr);
                        return -EINVAL;
                }
-               ret = check_ctrlrecip(ps, dr->bRequestType,
+               ret = check_ctrlrecip(ps, dr->bRequestType, dr->bRequest,
                                      le16_to_cpup(&dr->wIndex));
                if (ret) {
                        kfree(dr);
@@ -1037,15 +1133,10 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,
                        is_in = 0;
                        uurb->endpoint &= ~USB_DIR_IN;
                }
-               if (!access_ok(is_in ? VERIFY_WRITE : VERIFY_READ,
-                               uurb->buffer, uurb->buffer_length)) {
-                       kfree(dr);
-                       return -EFAULT;
-               }
-               snoop(&ps->dev->dev, "control urb: bRequest=%02x "
-                       "bRrequestType=%02x wValue=%04x "
+               snoop(&ps->dev->dev, "control urb: bRequestType=%02x "
+                       "bRequest=%02x wValue=%04x "
                        "wIndex=%04x wLength=%04x\n",
-                       dr->bRequest, dr->bRequestType,
+                       dr->bRequestType, dr->bRequest,
                        __le16_to_cpup(&dr->wValue),
                        __le16_to_cpup(&dr->wIndex),
                        __le16_to_cpup(&dr->wLength));
@@ -1056,15 +1147,23 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,
                case USB_ENDPOINT_XFER_CONTROL:
                case USB_ENDPOINT_XFER_ISOC:
                        return -EINVAL;
-               /* allow single-shot interrupt transfers, at bogus rates */
+               case USB_ENDPOINT_XFER_INT:
+                       /* allow single-shot interrupt transfers */
+                       uurb->type = USBDEVFS_URB_TYPE_INTERRUPT;
+                       goto interrupt_urb;
                }
                uurb->number_of_packets = 0;
                if (uurb->buffer_length > MAX_USBFS_BUFFER_SIZE)
                        return -EINVAL;
-               if (!access_ok(is_in ? VERIFY_WRITE : VERIFY_READ,
-                               uurb->buffer, uurb->buffer_length))
-                       return -EFAULT;
-               snoop(&ps->dev->dev, "bulk urb\n");
+               break;
+
+       case USBDEVFS_URB_TYPE_INTERRUPT:
+               if (!usb_endpoint_xfer_int(&ep->desc))
+                       return -EINVAL;
+ interrupt_urb:
+               uurb->number_of_packets = 0;
+               if (uurb->buffer_length > MAX_USBFS_BUFFER_SIZE)
+                       return -EINVAL;
                break;
 
        case USBDEVFS_URB_TYPE_ISO:
@@ -1091,42 +1190,47 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,
                        }
                        totlen += isopkt[u].length;
                }
-               if (totlen > 32768) {
+               /* 3072 * 64 microframes */
+               if (totlen > 196608) {
                        kfree(isopkt);
                        return -EINVAL;
                }
                uurb->buffer_length = totlen;
-               snoop(&ps->dev->dev, "iso urb\n");
-               break;
-
-       case USBDEVFS_URB_TYPE_INTERRUPT:
-               uurb->number_of_packets = 0;
-               if (!usb_endpoint_xfer_int(&ep->desc))
-                       return -EINVAL;
-               if (uurb->buffer_length > MAX_USBFS_BUFFER_SIZE)
-                       return -EINVAL;
-               if (!access_ok(is_in ? VERIFY_WRITE : VERIFY_READ,
-                               uurb->buffer, uurb->buffer_length))
-                       return -EFAULT;
-               snoop(&ps->dev->dev, "interrupt urb\n");
                break;
 
        default:
                return -EINVAL;
        }
-       as = alloc_async(uurb->number_of_packets);
-       if (!as) {
+       if (uurb->buffer_length > 0 &&
+                       !access_ok(is_in ? VERIFY_WRITE : VERIFY_READ,
+                               uurb->buffer, uurb->buffer_length)) {
                kfree(isopkt);
                kfree(dr);
-               return -ENOMEM;
+               return -EFAULT;
        }
-       as->urb->transfer_buffer = kmalloc(uurb->buffer_length, GFP_KERNEL);
-       if (!as->urb->transfer_buffer) {
+       as = alloc_async(uurb->number_of_packets);
+       if (!as) {
                kfree(isopkt);
                kfree(dr);
-               free_async(as);
                return -ENOMEM;
        }
+       if (uurb->buffer_length > 0) {
+               as->urb->transfer_buffer = kmalloc(uurb->buffer_length,
+                               GFP_KERNEL);
+               if (!as->urb->transfer_buffer) {
+                       kfree(isopkt);
+                       kfree(dr);
+                       free_async(as);
+                       return -ENOMEM;
+               }
+               /* Isochronous input data may end up being discontiguous
+                * if some of the packets are short.  Clear the buffer so
+                * that the gaps don't leak kernel data to userspace.
+                */
+               if (is_in && uurb->type == USBDEVFS_URB_TYPE_ISO)
+                       memset(as->urb->transfer_buffer, 0,
+                                       uurb->buffer_length);
+       }
        as->urb->dev = ps->dev;
        as->urb->pipe = (uurb->type << 30) |
                        __create_pipe(ps->dev, uurb->endpoint & 0xf) |
@@ -1168,7 +1272,7 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,
        kfree(isopkt);
        as->ps = ps;
        as->userurb = arg;
-       if (uurb->endpoint & USB_DIR_IN)
+       if (is_in && uurb->buffer_length > 0)
                as->userbuffer = uurb->buffer;
        else
                as->userbuffer = NULL;
@@ -1178,18 +1282,55 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,
        as->uid = cred->uid;
        as->euid = cred->euid;
        security_task_getsecid(current, &as->secid);
-       if (!is_in) {
+       if (!is_in && uurb->buffer_length > 0) {
                if (copy_from_user(as->urb->transfer_buffer, uurb->buffer,
-                               as->urb->transfer_buffer_length)) {
+                               uurb->buffer_length)) {
                        free_async(as);
                        return -EFAULT;
                }
        }
-       snoop_urb(as->urb, as->userurb);
+       snoop_urb(ps->dev, as->userurb, as->urb->pipe,
+                       as->urb->transfer_buffer_length, 0, SUBMIT,
+                       is_in ? NULL : as->urb->transfer_buffer,
+                               uurb->buffer_length);
        async_newpending(as);
-       if ((ret = usb_submit_urb(as->urb, GFP_KERNEL))) {
+
+       if (usb_endpoint_xfer_bulk(&ep->desc)) {
+               spin_lock_irq(&ps->lock);
+
+               /* Not exactly the endpoint address; the direction bit is
+                * shifted to the 0x10 position so that the value will be
+                * between 0 and 31.
+                */
+               as->bulk_addr = usb_endpoint_num(&ep->desc) |
+                       ((ep->desc.bEndpointAddress & USB_ENDPOINT_DIR_MASK)
+                               >> 3);
+
+               /* If this bulk URB is the start of a new transfer, re-enable
+                * the endpoint.  Otherwise mark it as a continuation URB.
+                */
+               if (uurb->flags & USBDEVFS_URB_BULK_CONTINUATION)
+                       as->bulk_status = AS_CONTINUATION;
+               else
+                       ps->disabled_bulk_eps &= ~(1 << as->bulk_addr);
+
+               /* Don't accept continuation URBs if the endpoint is
+                * disabled because of an earlier error.
+                */
+               if (ps->disabled_bulk_eps & (1 << as->bulk_addr))
+                       ret = -EREMOTEIO;
+               else
+                       ret = usb_submit_urb(as->urb, GFP_ATOMIC);
+               spin_unlock_irq(&ps->lock);
+       } else {
+               ret = usb_submit_urb(as->urb, GFP_KERNEL);
+       }
+
+       if (ret) {
                dev_printk(KERN_DEBUG, &ps->dev->dev,
                           "usbfs: usb_submit_urb returned %d\n", ret);
+               snoop_urb(ps->dev, as->userurb, as->urb->pipe,
+                               0, ret, COMPLETE, NULL, 0);
                async_removepending(as);
                free_async(as);
                return ret;
@@ -1227,33 +1368,38 @@ static int processcompl(struct async *as, void __user * __user *arg)
        void __user *addr = as->userurb;
        unsigned int i;
 
-       if (as->userbuffer)
-               if (copy_to_user(as->userbuffer, urb->transfer_buffer,
-                                urb->transfer_buffer_length))
-                       return -EFAULT;
+       if (as->userbuffer && urb->actual_length) {
+               if (urb->number_of_packets > 0)         /* Isochronous */
+                       i = urb->transfer_buffer_length;
+               else                                    /* Non-Isoc */
+                       i = urb->actual_length;
+               if (copy_to_user(as->userbuffer, urb->transfer_buffer, i))
+                       goto err_out;
+       }
        if (put_user(as->status, &userurb->status))
-               return -EFAULT;
+               goto err_out;
        if (put_user(urb->actual_length, &userurb->actual_length))
-               return -EFAULT;
+               goto err_out;
        if (put_user(urb->error_count, &userurb->error_count))
-               return -EFAULT;
+               goto err_out;
 
        if (usb_endpoint_xfer_isoc(&urb->ep->desc)) {
                for (i = 0; i < urb->number_of_packets; i++) {
                        if (put_user(urb->iso_frame_desc[i].actual_length,
                                     &userurb->iso_frame_desc[i].actual_length))
-                               return -EFAULT;
+                               goto err_out;
                        if (put_user(urb->iso_frame_desc[i].status,
                                     &userurb->iso_frame_desc[i].status))
-                               return -EFAULT;
+                               goto err_out;
                }
        }
 
-       free_async(as);
-
        if (put_user(addr, (void __user * __user *)arg))
                return -EFAULT;
        return 0;
+
+err_out:
+       return -EFAULT;
 }
 
 static struct async *reap_as(struct dev_state *ps)
@@ -1282,8 +1428,11 @@ static struct async *reap_as(struct dev_state *ps)
 static int proc_reapurb(struct dev_state *ps, void __user *arg)
 {
        struct async *as = reap_as(ps);
-       if (as)
-               return processcompl(as, (void __user * __user *)arg);
+       if (as) {
+               int retval = processcompl(as, (void __user * __user *)arg);
+               free_async(as);
+               return retval;
+       }
        if (signal_pending(current))
                return -EINTR;
        return -EIO;
@@ -1291,20 +1440,66 @@ static int proc_reapurb(struct dev_state *ps, void __user *arg)
 
 static int proc_reapurbnonblock(struct dev_state *ps, void __user *arg)
 {
+       int retval;
        struct async *as;
 
-       if (!(as = async_getcompleted(ps)))
-               return -EAGAIN;
-       return processcompl(as, (void __user * __user *)arg);
+       as = async_getcompleted(ps);
+       retval = -EAGAIN;
+       if (as) {
+               retval = processcompl(as, (void __user * __user *)arg);
+               free_async(as);
+       }
+       return retval;
 }
 
 #ifdef CONFIG_COMPAT
+static int proc_control_compat(struct dev_state *ps,
+                               struct usbdevfs_ctrltransfer32 __user *p32)
+{
+        struct usbdevfs_ctrltransfer __user *p;
+        __u32 udata;
+        p = compat_alloc_user_space(sizeof(*p));
+        if (copy_in_user(p, p32, (sizeof(*p32) - sizeof(compat_caddr_t))) ||
+            get_user(udata, &p32->data) ||
+           put_user(compat_ptr(udata), &p->data))
+               return -EFAULT;
+        return proc_control(ps, p);
+}
+
+static int proc_bulk_compat(struct dev_state *ps,
+                       struct usbdevfs_bulktransfer32 __user *p32)
+{
+        struct usbdevfs_bulktransfer __user *p;
+        compat_uint_t n;
+        compat_caddr_t addr;
+
+        p = compat_alloc_user_space(sizeof(*p));
+
+        if (get_user(n, &p32->ep) || put_user(n, &p->ep) ||
+            get_user(n, &p32->len) || put_user(n, &p->len) ||
+            get_user(n, &p32->timeout) || put_user(n, &p->timeout) ||
+            get_user(addr, &p32->data) || put_user(compat_ptr(addr), &p->data))
+                return -EFAULT;
+
+        return proc_bulk(ps, p);
+}
+static int proc_disconnectsignal_compat(struct dev_state *ps, void __user *arg)
+{
+       struct usbdevfs_disconnectsignal32 ds;
+
+       if (copy_from_user(&ds, arg, sizeof(ds)))
+               return -EFAULT;
+       ps->discsignr = ds.signr;
+       ps->disccontext = compat_ptr(ds.context);
+       return 0;
+}
 
 static int get_urb32(struct usbdevfs_urb *kurb,
                     struct usbdevfs_urb32 __user *uurb)
 {
        __u32  uptr;
-       if (get_user(kurb->type, &uurb->type) ||
+       if (!access_ok(VERIFY_READ, uurb, sizeof(*uurb)) ||
+           __get_user(kurb->type, &uurb->type) ||
            __get_user(kurb->endpoint, &uurb->endpoint) ||
            __get_user(kurb->status, &uurb->status) ||
            __get_user(kurb->flags, &uurb->flags) ||
@@ -1345,9 +1540,9 @@ static int processcompl_compat(struct async *as, void __user * __user *arg)
        void __user *addr = as->userurb;
        unsigned int i;
 
-       if (as->userbuffer)
+       if (as->userbuffer && urb->actual_length)
                if (copy_to_user(as->userbuffer, urb->transfer_buffer,
-                                urb->transfer_buffer_length))
+                                urb->actual_length))
                        return -EFAULT;
        if (put_user(as->status, &userurb->status))
                return -EFAULT;
@@ -1367,7 +1562,6 @@ static int processcompl_compat(struct async *as, void __user * __user *arg)
                }
        }
 
-       free_async(as);
        if (put_user(ptr_to_compat(addr), (u32 __user *)arg))
                return -EFAULT;
        return 0;
@@ -1376,8 +1570,11 @@ static int processcompl_compat(struct async *as, void __user * __user *arg)
 static int proc_reapurb_compat(struct dev_state *ps, void __user *arg)
 {
        struct async *as = reap_as(ps);
-       if (as)
-               return processcompl_compat(as, (void __user * __user *)arg);
+       if (as) {
+               int retval = processcompl_compat(as, (void __user * __user *)arg);
+               free_async(as);
+               return retval;
+       }
        if (signal_pending(current))
                return -EINTR;
        return -EIO;
@@ -1385,13 +1582,19 @@ static int proc_reapurb_compat(struct dev_state *ps, void __user *arg)
 
 static int proc_reapurbnonblock_compat(struct dev_state *ps, void __user *arg)
 {
+       int retval;
        struct async *as;
 
-       if (!(as = async_getcompleted(ps)))
-               return -EAGAIN;
-       return processcompl_compat(as, (void __user * __user *)arg);
+       retval = -EAGAIN;
+       as = async_getcompleted(ps);
+       if (as) {
+               retval = processcompl_compat(as, (void __user * __user *)arg);
+               free_async(as);
+       }
+       return retval;
 }
 
+
 #endif
 
 static int proc_disconnectsignal(struct dev_state *ps, void __user *arg)
@@ -1482,10 +1685,10 @@ static int proc_ioctl(struct dev_state *ps, struct usbdevfs_ioctl *ctl)
        default:
                if (intf->dev.driver)
                        driver = to_usb_driver(intf->dev.driver);
-               if (driver == NULL || driver->ioctl == NULL) {
+               if (driver == NULL || driver->unlocked_ioctl == NULL) {
                        retval = -ENOTTY;
                } else {
-                       retval = driver->ioctl(intf, ctl->ioctl_code, buf);
+                       retval = driver->unlocked_ioctl(intf, ctl->ioctl_code, buf);
                        if (retval == -ENOIOCTLCMD)
                                retval = -ENOTTY;
                }
@@ -1519,8 +1722,9 @@ static int proc_ioctl_compat(struct dev_state *ps, compat_uptr_t arg)
        u32 udata;
 
        uioc = compat_ptr((long)arg);
-       if (get_user(ctrl.ifno, &uioc->ifno) ||
-           get_user(ctrl.ioctl_code, &uioc->ioctl_code) ||
+       if (!access_ok(VERIFY_READ, uioc, sizeof(*uioc)) ||
+           __get_user(ctrl.ifno, &uioc->ifno) ||
+           __get_user(ctrl.ioctl_code, &uioc->ioctl_code) ||
            __get_user(udata, &uioc->data))
                return -EFAULT;
        ctrl.data = compat_ptr(udata);
@@ -1529,21 +1733,45 @@ static int proc_ioctl_compat(struct dev_state *ps, compat_uptr_t arg)
 }
 #endif
 
+static int proc_claim_port(struct dev_state *ps, void __user *arg)
+{
+       unsigned portnum;
+       int rc;
+
+       if (get_user(portnum, (unsigned __user *) arg))
+               return -EFAULT;
+       rc = usb_hub_claim_port(ps->dev, portnum, ps);
+       if (rc == 0)
+               snoop(&ps->dev->dev, "port %d claimed by process %d: %s\n",
+                       portnum, task_pid_nr(current), current->comm);
+       return rc;
+}
+
+static int proc_release_port(struct dev_state *ps, void __user *arg)
+{
+       unsigned portnum;
+
+       if (get_user(portnum, (unsigned __user *) arg))
+               return -EFAULT;
+       return usb_hub_release_port(ps->dev, portnum, ps);
+}
+
 /*
  * NOTE:  All requests here that have interface numbers as parameters
  * are assuming that somehow the configuration has been prevented from
  * changing.  But there's no mechanism to ensure that...
  */
-static int usbdev_ioctl(struct inode *inode, struct file *file,
-                       unsigned int cmd, unsigned long arg)
+static long usbdev_do_ioctl(struct file *file, unsigned int cmd,
+                               void __user *p)
 {
        struct dev_state *ps = file->private_data;
+       struct inode *inode = file->f_path.dentry->d_inode;
        struct usb_device *dev = ps->dev;
-       void __user *p = (void __user *)arg;
        int ret = -ENOTTY;
 
        if (!(file->f_mode & FMODE_WRITE))
                return -EPERM;
+
        usb_lock_device(dev);
        if (!connected(ps)) {
                usb_unlock_device(dev);
@@ -1612,6 +1840,24 @@ static int usbdev_ioctl(struct inode *inode, struct file *file,
                break;
 
 #ifdef CONFIG_COMPAT
+       case USBDEVFS_CONTROL32:
+               snoop(&dev->dev, "%s: CONTROL32\n", __func__);
+               ret = proc_control_compat(ps, p);
+               if (ret >= 0)
+                       inode->i_mtime = CURRENT_TIME;
+               break;
+
+       case USBDEVFS_BULK32:
+               snoop(&dev->dev, "%s: BULK32\n", __func__);
+               ret = proc_bulk_compat(ps, p);
+               if (ret >= 0)
+                       inode->i_mtime = CURRENT_TIME;
+               break;
+
+       case USBDEVFS_DISCSIGNAL32:
+               snoop(&dev->dev, "%s: DISCSIGNAL32\n", __func__);
+               ret = proc_disconnectsignal_compat(ps, p);
+               break;
 
        case USBDEVFS_SUBMITURB32:
                snoop(&dev->dev, "%s: SUBMITURB32\n", __func__);
@@ -1626,12 +1872,12 @@ static int usbdev_ioctl(struct inode *inode, struct file *file,
                break;
 
        case USBDEVFS_REAPURBNDELAY32:
-               snoop(&dev->dev, "%s: REAPURBDELAY32\n", __func__);
+               snoop(&dev->dev, "%s: REAPURBNDELAY32\n", __func__);
                ret = proc_reapurbnonblock_compat(ps, p);
                break;
 
        case USBDEVFS_IOCTL32:
-               snoop(&dev->dev, "%s: IOCTL\n", __func__);
+               snoop(&dev->dev, "%s: IOCTL32\n", __func__);
                ret = proc_ioctl_compat(ps, ptr_to_compat(p));
                break;
 #endif
@@ -1647,7 +1893,7 @@ static int usbdev_ioctl(struct inode *inode, struct file *file,
                break;
 
        case USBDEVFS_REAPURBNDELAY:
-               snoop(&dev->dev, "%s: REAPURBDELAY\n", __func__);
+               snoop(&dev->dev, "%s: REAPURBNDELAY\n", __func__);
                ret = proc_reapurbnonblock(ps, p);
                break;
 
@@ -1670,6 +1916,16 @@ static int usbdev_ioctl(struct inode *inode, struct file *file,
                snoop(&dev->dev, "%s: IOCTL\n", __func__);
                ret = proc_ioctl_default(ps, p);
                break;
+
+       case USBDEVFS_CLAIM_PORT:
+               snoop(&dev->dev, "%s: CLAIM_PORT\n", __func__);
+               ret = proc_claim_port(ps, p);
+               break;
+
+       case USBDEVFS_RELEASE_PORT:
+               snoop(&dev->dev, "%s: RELEASE_PORT\n", __func__);
+               ret = proc_release_port(ps, p);
+               break;
        }
        usb_unlock_device(dev);
        if (ret >= 0)
@@ -1677,6 +1933,28 @@ static int usbdev_ioctl(struct inode *inode, struct file *file,
        return ret;
 }
 
+static long usbdev_ioctl(struct file *file, unsigned int cmd,
+                       unsigned long arg)
+{
+       int ret;
+
+       ret = usbdev_do_ioctl(file, cmd, (void __user *)arg);
+
+       return ret;
+}
+
+#ifdef CONFIG_COMPAT
+static long usbdev_compat_ioctl(struct file *file, unsigned int cmd,
+                       unsigned long arg)
+{
+       int ret;
+
+       ret = usbdev_do_ioctl(file, cmd, compat_ptr(arg));
+
+       return ret;
+}
+#endif
+
 /* No kernel lock - fine */
 static unsigned int usbdev_poll(struct file *file,
                                struct poll_table_struct *wait)
@@ -1693,13 +1971,16 @@ static unsigned int usbdev_poll(struct file *file,
 }
 
 const struct file_operations usbdev_file_operations = {
-       .owner =        THIS_MODULE,
-       .llseek =       usbdev_lseek,
-       .read =         usbdev_read,
-       .poll =         usbdev_poll,
-       .ioctl =        usbdev_ioctl,
-       .open =         usbdev_open,
-       .release =      usbdev_release,
+       .owner =          THIS_MODULE,
+       .llseek =         usbdev_lseek,
+       .read =           usbdev_read,
+       .poll =           usbdev_poll,
+       .unlocked_ioctl = usbdev_ioctl,
+#ifdef CONFIG_COMPAT
+       .compat_ioctl =   usbdev_compat_ioctl,
+#endif
+       .open =           usbdev_open,
+       .release =        usbdev_release,
 };
 
 static void usbdev_remove(struct usb_device *udev)