[PATCH] USB: fix check_ctrlrecip to allow control transfers in state ADDRESS
[pandora-kernel.git] / drivers / usb / core / devio.c
index 487ff67..545da37 100644 (file)
@@ -46,6 +46,7 @@
 #include <linux/usb.h>
 #include <linux/usbdevice_fs.h>
 #include <linux/cdev.h>
+#include <linux/notifier.h>
 #include <asm/uaccess.h>
 #include <asm/byteorder.h>
 #include <linux/moduleparam.h>
@@ -133,26 +134,21 @@ static ssize_t usbdev_read(struct file *file, char __user *buf, size_t nbytes, l
        }
 
        if (pos < sizeof(struct usb_device_descriptor)) {
-               struct usb_device_descriptor *desc = kmalloc(sizeof(*desc), GFP_KERNEL);
-               if (!desc) {
-                       ret = -ENOMEM;
-                       goto err;
-               }
-               memcpy(desc, &dev->descriptor, sizeof(dev->descriptor));
-               le16_to_cpus(&desc->bcdUSB);
-               le16_to_cpus(&desc->idVendor);
-               le16_to_cpus(&desc->idProduct);
-               le16_to_cpus(&desc->bcdDevice);
+               struct usb_device_descriptor temp_desc ; /* 18 bytes - fits on the stack */
+
+               memcpy(&temp_desc, &dev->descriptor, sizeof(dev->descriptor));
+               le16_to_cpus(&temp_desc.bcdUSB);
+               le16_to_cpus(&temp_desc.idVendor);
+               le16_to_cpus(&temp_desc.idProduct);
+               le16_to_cpus(&temp_desc.bcdDevice);
 
                len = sizeof(struct usb_device_descriptor) - pos;
                if (len > nbytes)
                        len = nbytes;
-               if (copy_to_user(buf, ((char *)desc) + pos, len)) {
-                       kfree(desc);
+               if (copy_to_user(buf, ((char *)&temp_desc) + pos, len)) {
                        ret = -EFAULT;
                        goto err;
                }
-               kfree(desc);
 
                *ppos += len;
                buf += len;
@@ -209,10 +205,10 @@ err:
 static struct async *alloc_async(unsigned int numisoframes)
 {
         unsigned int assize = sizeof(struct async) + numisoframes * sizeof(struct usb_iso_packet_descriptor);
-        struct async *as = kmalloc(assize, GFP_KERNEL);
+        struct async *as = kzalloc(assize, GFP_KERNEL);
+
         if (!as)
                 return NULL;
-        memset(as, 0, assize);
        as->urb = usb_alloc_urb(numisoframes, GFP_KERNEL);
        if (!as->urb) {
                kfree(as);
@@ -279,6 +275,28 @@ static inline struct async *async_getpending(struct dev_state *ps, void __user *
         return NULL;
 }
 
+static void snoop_urb(struct urb *urb, void __user *userurb)
+{
+       int j;
+       unsigned char *data = urb->transfer_buffer;
+
+       if (!usbfs_snoop)
+               return;
+
+       if (urb->pipe & USB_DIR_IN)
+               dev_info(&urb->dev->dev, "direction=IN\n");
+       else
+               dev_info(&urb->dev->dev, "direction=OUT\n");
+       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");
+}
+
 static void async_completed(struct urb *urb, struct pt_regs *regs)
 {
         struct async *as = (struct async *)urb->context;
@@ -296,7 +314,9 @@ static void async_completed(struct urb *urb, struct pt_regs *regs)
                kill_proc_info_as_uid(as->signr, &sinfo, as->pid, as->uid, 
                                      as->euid);
        }
-        wake_up(&ps->wait);
+       snoop(&urb->dev->dev, "urb complete\n");
+       snoop_urb(urb, as->userurb);
+       wake_up(&ps->wait);
 }
 
 static void destroy_async (struct dev_state *ps, struct list_head *list)
@@ -377,7 +397,6 @@ static void driver_disconnect(struct usb_interface *intf)
 }
 
 struct usb_driver usbfs_driver = {
-       .owner =        THIS_MODULE,
        .name =         "usbfs",
        .probe =        driver_probe,
        .disconnect =   driver_disconnect,
@@ -474,7 +493,8 @@ static int check_ctrlrecip(struct dev_state *ps, unsigned int requesttype, unsig
 {
        int ret = 0;
 
-       if (ps->dev->state != USB_STATE_CONFIGURED)
+       if (ps->dev->state != USB_STATE_ADDRESS
+        && ps->dev->state != USB_STATE_CONFIGURED)
                return -EHOSTUNREACH;
        if (USB_TYPE_VENDOR == (USB_TYPE_MASK & requesttype))
                return 0;
@@ -493,6 +513,23 @@ static int check_ctrlrecip(struct dev_state *ps, unsigned int requesttype, unsig
        return ret;
 }
 
+static struct usb_device *usbdev_lookup_minor(int minor)
+{
+       struct class_device *class_dev;
+       struct usb_device *dev = NULL;
+
+       down(&usb_device_class->sem);
+       list_for_each_entry(class_dev, &usb_device_class->children, node) {
+               if (class_dev->devt == MKDEV(USB_DEVICE_MAJOR, minor)) {
+                       dev = class_dev->class_data;
+                       break;
+               }
+       }
+       up(&usb_device_class->sem);
+
+       return dev;
+};
+
 /*
  * file operations
  */
@@ -601,7 +638,7 @@ static int proc_control(struct dev_state *ps, void __user *arg)
                        if (usbfs_snoop) {
                                dev_info(&dev->dev, "control read: data ");
                                for (j = 0; j < i; ++j)
-                                       printk ("%02x ", (unsigned char)(tbuf)[j]);
+                                       printk("%02x ", (unsigned char)(tbuf)[j]);
                                printk("\n");
                        }
                        if (copy_to_user(ctrl.data, tbuf, i)) {
@@ -624,7 +661,7 @@ static int proc_control(struct dev_state *ps, void __user *arg)
                if (usbfs_snoop) {
                        dev_info(&dev->dev, "control write: data: ");
                        for (j = 0; j < ctrl.wLength; ++j)
-                               printk ("%02x ", (unsigned char)(tbuf)[j]);
+                               printk("%02x ", (unsigned char)(tbuf)[j]);
                        printk("\n");
                }
                usb_unlock_device(dev);
@@ -649,7 +686,7 @@ static int proc_bulk(struct dev_state *ps, void __user *arg)
        unsigned int tmo, len1, pipe;
        int len2;
        unsigned char *tbuf;
-       int i, ret;
+       int i, j, ret;
 
        if (copy_from_user(&bulk, arg, sizeof(bulk)))
                return -EFAULT;
@@ -674,10 +711,18 @@ 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);
                usb_unlock_device(dev);
                i = usb_bulk_msg(dev, pipe, tbuf, len1, &len2, tmo);
                usb_lock_device(dev);
                if (!i && len2) {
+                       if (usbfs_snoop) {
+                               dev_info(&dev->dev, "bulk read: data ");
+                               for (j = 0; j < len2; ++j)
+                                       printk("%02x ", (unsigned char)(tbuf)[j]);
+                               printk("\n");
+                       }
                        if (copy_to_user(bulk.data, tbuf, len2)) {
                                kfree(tbuf);
                                return -EFAULT;
@@ -690,6 +735,14 @@ 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");
+               }
                usb_unlock_device(dev);
                i = usb_bulk_msg(dev, pipe, tbuf, len1, &len2, tmo);
                usb_lock_device(dev);
@@ -835,7 +888,6 @@ static int proc_setconfig(struct dev_state *ps, void __user *arg)
        return status;
 }
 
-
 static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,
                             struct usbdevfs_iso_packet_desc __user *iso_frame_desc,
                             void __user *arg)
@@ -896,6 +948,7 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,
                        kfree(dr);
                        return -EFAULT;
                }
+               snoop(&ps->dev->dev, "control urb\n");
                break;
 
        case USBDEVFS_URB_TYPE_BULK:
@@ -910,6 +963,7 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,
                        return -EINVAL;
                if (!access_ok((uurb->endpoint & USB_DIR_IN) ? VERIFY_WRITE : VERIFY_READ, uurb->buffer, uurb->buffer_length))
                        return -EFAULT;
+               snoop(&ps->dev->dev, "bulk urb\n");
                break;
 
        case USBDEVFS_URB_TYPE_ISO:
@@ -939,6 +993,7 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,
                        return -EINVAL;
                }
                uurb->buffer_length = totlen;
+               snoop(&ps->dev->dev, "iso urb\n");
                break;
 
        case USBDEVFS_URB_TYPE_INTERRUPT:
@@ -954,6 +1009,7 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,
                        return -EINVAL;
                if (!access_ok((uurb->endpoint & USB_DIR_IN) ? VERIFY_WRITE : VERIFY_READ, uurb->buffer, uurb->buffer_length))
                        return -EFAULT;
+               snoop(&ps->dev->dev, "interrupt urb\n");
                break;
 
        default:
@@ -1003,6 +1059,8 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,
                        return -EFAULT;
                }
        }
+       snoop(&as->urb->dev->dev, "submit urb\n");
+       snoop_urb(as->urb, as->userurb);
         async_newpending(as);
         if ((ret = usb_submit_urb(as->urb, GFP_KERNEL))) {
                dev_printk(KERN_DEBUG, &ps->dev->dev, "usbfs: usb_submit_urb returned %d\n", ret);
@@ -1238,23 +1296,20 @@ static int proc_releaseinterface(struct dev_state *ps, void __user *arg)
        return 0;
 }
 
-static int proc_ioctl (struct dev_state *ps, void __user *arg)
+static int proc_ioctl(struct dev_state *ps, struct usbdevfs_ioctl *ctl)
 {
-       struct usbdevfs_ioctl   ctrl;
        int                     size;
        void                    *buf = NULL;
        int                     retval = 0;
        struct usb_interface    *intf = NULL;
        struct usb_driver       *driver = NULL;
 
-       /* get input parameters and alloc buffer */
-       if (copy_from_user(&ctrl, arg, sizeof (ctrl)))
-               return -EFAULT;
-       if ((size = _IOC_SIZE (ctrl.ioctl_code)) > 0) {
+       /* alloc buffer */
+       if ((size = _IOC_SIZE (ctl->ioctl_code)) > 0) {
                if ((buf = kmalloc (size, GFP_KERNEL)) == NULL)
                        return -ENOMEM;
-               if ((_IOC_DIR(ctrl.ioctl_code) & _IOC_WRITE)) {
-                       if (copy_from_user (buf, ctrl.data, size)) {
+               if ((_IOC_DIR(ctl->ioctl_code) & _IOC_WRITE)) {
+                       if (copy_from_user (buf, ctl->data, size)) {
                                kfree(buf);
                                return -EFAULT;
                        }
@@ -1270,9 +1325,9 @@ static int proc_ioctl (struct dev_state *ps, void __user *arg)
 
        if (ps->dev->state != USB_STATE_CONFIGURED)
                retval = -EHOSTUNREACH;
-       else if (!(intf = usb_ifnum_to_if (ps->dev, ctrl.ifno)))
+       else if (!(intf = usb_ifnum_to_if (ps->dev, ctl->ifno)))
                retval = -EINVAL;
-       else switch (ctrl.ioctl_code) {
+       else switch (ctl->ioctl_code) {
 
        /* disconnect kernel driver from interface */
        case USBDEVFS_DISCONNECT:
@@ -1290,9 +1345,7 @@ static int proc_ioctl (struct dev_state *ps, void __user *arg)
        /* let kernel drivers try to (re)bind to the interface */
        case USBDEVFS_CONNECT:
                usb_unlock_device(ps->dev);
-               usb_lock_all_devices();
                bus_rescan_devices(intf->dev.bus);
-               usb_unlock_all_devices();
                usb_lock_device(ps->dev);
                break;
 
@@ -1304,7 +1357,7 @@ static int proc_ioctl (struct dev_state *ps, void __user *arg)
                if (driver == NULL || driver->ioctl == NULL) {
                        retval = -ENOTTY;
                } else {
-                       retval = driver->ioctl (intf, ctrl.ioctl_code, buf);
+                       retval = driver->ioctl (intf, ctl->ioctl_code, buf);
                        if (retval == -ENOIOCTLCMD)
                                retval = -ENOTTY;
                }
@@ -1313,15 +1366,42 @@ static int proc_ioctl (struct dev_state *ps, void __user *arg)
 
        /* cleanup and return */
        if (retval >= 0
-                       && (_IOC_DIR (ctrl.ioctl_code) & _IOC_READ) != 0
+                       && (_IOC_DIR (ctl->ioctl_code) & _IOC_READ) != 0
                        && size > 0
-                       && copy_to_user (ctrl.data, buf, size) != 0)
+                       && copy_to_user (ctl->data, buf, size) != 0)
                retval = -EFAULT;
 
        kfree(buf);
        return retval;
 }
 
+static int proc_ioctl_default(struct dev_state *ps, void __user *arg)
+{
+       struct usbdevfs_ioctl   ctrl;
+
+       if (copy_from_user(&ctrl, arg, sizeof (ctrl)))
+               return -EFAULT;
+       return proc_ioctl(ps, &ctrl);
+}
+
+#ifdef CONFIG_COMPAT
+static int proc_ioctl_compat(struct dev_state *ps, compat_uptr_t arg)
+{
+       struct usbdevfs_ioctl32 __user *uioc;
+       struct usbdevfs_ioctl ctrl;
+       u32 udata;
+
+       uioc = compat_ptr((long)arg);
+       if (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);
+
+       return proc_ioctl(ps, &ctrl);
+}
+#endif
+
 /*
  * NOTE:  All requests here that have interface numbers as parameters
  * are assuming that somehow the configuration has been prevented from
@@ -1422,6 +1502,10 @@ static int usbdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd
                ret = proc_reapurbnonblock_compat(ps, p);
                break;
 
+       case USBDEVFS_IOCTL32:
+               snoop(&dev->dev, "%s: IOCTL\n", __FUNCTION__);
+               ret = proc_ioctl_compat(ps, (compat_uptr_t)(long)p);
+               break;
 #endif
 
        case USBDEVFS_DISCARDURB:
@@ -1456,7 +1540,7 @@ static int usbdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd
 
        case USBDEVFS_IOCTL:
                snoop(&dev->dev, "%s: IOCTL\n", __FUNCTION__);
-               ret = proc_ioctl(ps, p);
+               ret = proc_ioctl_default(ps, p);
                break;
        }
        usb_unlock_device(dev);
@@ -1488,39 +1572,40 @@ struct file_operations usbfs_device_file_operations = {
        .release =      usbdev_release,
 };
 
-struct usb_device *usbdev_lookup_minor(int minor)
-{
-       struct class_device *class_dev;
-       struct usb_device *dev = NULL;
-
-       down(&usb_device_class->sem);
-       list_for_each_entry(class_dev, &usb_device_class->children, node) {
-               if (class_dev->devt == MKDEV(USB_DEVICE_MAJOR, minor)) {
-                       dev = class_dev->class_data;
-                       break;
-               }
-       }
-       up(&usb_device_class->sem);
-
-       return dev;
-};
-
-void usbdev_add(struct usb_device *dev)
+static void usbdev_add(struct usb_device *dev)
 {
        int minor = ((dev->bus->busnum-1) * 128) + (dev->devnum-1);
 
-       dev->class_dev = class_device_create(usb_device_class,
+       dev->class_dev = class_device_create(usb_device_class, NULL,
                                MKDEV(USB_DEVICE_MAJOR, minor), &dev->dev,
                                "usbdev%d.%d", dev->bus->busnum, dev->devnum);
 
        dev->class_dev->class_data = dev;
 }
 
-void usbdev_remove(struct usb_device *dev)
+static void usbdev_remove(struct usb_device *dev)
 {
        class_device_unregister(dev->class_dev);
 }
 
+static int usbdev_notify(struct notifier_block *self, unsigned long action,
+                        void *dev)
+{
+       switch (action) {
+       case USB_DEVICE_ADD:
+               usbdev_add(dev);
+               break;
+       case USB_DEVICE_REMOVE:
+               usbdev_remove(dev);
+               break;
+       }
+       return NOTIFY_OK;
+}
+
+static struct notifier_block usbdev_nb = {
+       .notifier_call =        usbdev_notify,
+};
+
 static struct cdev usb_device_cdev = {
        .kobj   = {.name = "usb_device", },
        .owner  = THIS_MODULE,
@@ -1540,24 +1625,32 @@ int __init usbdev_init(void)
        retval = cdev_add(&usb_device_cdev, USB_DEVICE_DEV, USB_DEVICE_MAX);
        if (retval) {
                err("unable to get usb_device major %d", USB_DEVICE_MAJOR);
-               unregister_chrdev_region(USB_DEVICE_DEV, USB_DEVICE_MAX);
-               goto out;
+               goto error_cdev;
        }
        usb_device_class = class_create(THIS_MODULE, "usb_device");
        if (IS_ERR(usb_device_class)) {
                err("unable to register usb_device class");
                retval = PTR_ERR(usb_device_class);
-               usb_device_class = NULL;
-               cdev_del(&usb_device_cdev);
-               unregister_chrdev_region(USB_DEVICE_DEV, USB_DEVICE_MAX);
+               goto error_class;
        }
 
+       usb_register_notify(&usbdev_nb);
+
 out:
        return retval;
+
+error_class:
+       usb_device_class = NULL;
+       cdev_del(&usb_device_cdev);
+
+error_cdev:
+       unregister_chrdev_region(USB_DEVICE_DEV, USB_DEVICE_MAX);
+       goto out;
 }
 
 void usbdev_cleanup(void)
 {
+       usb_unregister_notify(&usbdev_nb);
        class_destroy(usb_device_class);
        cdev_del(&usb_device_cdev);
        unregister_chrdev_region(USB_DEVICE_DEV, USB_DEVICE_MAX);