USB: quirky flash drive
[safe/jmp/linux-2.6] / drivers / usb / core / devio.c
index a94c63b..927a181 100644 (file)
@@ -57,7 +57,6 @@
 
 #define USB_MAXBUS                     64
 #define USB_DEVICE_MAX                 USB_MAXBUS * 128
-static struct class *usb_device_class;
 
 /* Mutual exclusion for removal, open, and release */
 DEFINE_MUTEX(usbfs_mutex);
@@ -65,7 +64,7 @@ DEFINE_MUTEX(usbfs_mutex);
 struct async {
        struct list_head asynclist;
        struct dev_state *ps;
-       pid_t pid;
+       struct pid *pid;
        uid_t uid, euid;
        unsigned int signr;
        unsigned int ifnum;
@@ -225,6 +224,7 @@ static struct async *alloc_async(unsigned int numisoframes)
 
 static void free_async(struct async *as)
 {
+       put_pid(as->pid);
        kfree(as->urb->transfer_buffer);
        kfree(as->urb->setup_packet);
        usb_free_urb(as->urb);
@@ -303,7 +303,7 @@ static void snoop_urb(struct urb *urb, void __user *userurb)
        printk("\n");
 }
 
-static void async_completed(struct urb *urb, struct pt_regs *regs)
+static void async_completed(struct urb *urb)
 {
         struct async *as = urb->context;
         struct dev_state *ps = as->ps;
@@ -317,7 +317,7 @@ static void async_completed(struct urb *urb, struct pt_regs *regs)
                sinfo.si_errno = as->urb->status;
                sinfo.si_code = SI_ASYNCIO;
                sinfo.si_addr = as->userurb;
-               kill_proc_info_as_uid(as->signr, &sinfo, as->pid, as->uid, 
+               kill_pid_info_as_uid(as->signr, &sinfo, as->pid, as->uid,
                                      as->euid, as->secid);
        }
        snoop(&urb->dev->dev, "urb complete\n");
@@ -420,14 +420,11 @@ static int claimintf(struct dev_state *ps, unsigned int ifnum)
        if (test_bit(ifnum, &ps->ifclaimed))
                return 0;
 
-       /* lock against other changes to driver bindings */
-       down_write(&usb_bus_type.subsys.rwsem);
        intf = usb_ifnum_to_if(dev, ifnum);
        if (!intf)
                err = -ENOENT;
        else
                err = usb_driver_claim_interface(&usbfs_driver, intf, ps);
-       up_write(&usb_bus_type.subsys.rwsem);
        if (err == 0)
                set_bit(ifnum, &ps->ifclaimed);
        return err;
@@ -443,8 +440,6 @@ static int releaseintf(struct dev_state *ps, unsigned int ifnum)
        if (ifnum >= 8*sizeof(ps->ifclaimed))
                return err;
        dev = ps->dev;
-       /* lock against other changes to driver bindings */
-       down_write(&usb_bus_type.subsys.rwsem);
        intf = usb_ifnum_to_if(dev, ifnum);
        if (!intf)
                err = -ENOENT;
@@ -452,7 +447,6 @@ static int releaseintf(struct dev_state *ps, unsigned int ifnum)
                usb_driver_release_interface(&usbfs_driver, intf);
                err = 0;
        }
-       up_write(&usb_bus_type.subsys.rwsem);
        return err;
 }
 
@@ -519,22 +513,25 @@ static int check_ctrlrecip(struct dev_state *ps, unsigned int requesttype, unsig
        return ret;
 }
 
-static struct usb_device *usbdev_lookup_minor(int minor)
+static int __match_minor(struct device *dev, void *data)
 {
-       struct class_device *class_dev;
-       struct usb_device *dev = NULL;
+       int minor = *((int *)data);
 
-       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);
+       if (dev->devt == MKDEV(USB_DEVICE_MAJOR, minor))
+               return 1;
+       return 0;
+}
 
-       return dev;
-};
+static struct usb_device *usbdev_lookup_by_minor(int minor)
+{
+       struct device *dev;
+
+       dev = bus_find_device(&usb_bus_type, NULL, &minor, __match_minor);
+       if (!dev)
+               return NULL;
+       put_device(dev);
+       return container_of(dev, struct usb_device, dev);
+}
 
 /*
  * file operations
@@ -553,14 +550,17 @@ static int usbdev_open(struct inode *inode, struct file *file)
                goto out;
 
        ret = -ENOENT;
-       /* check if we are called from a real node or usbfs */
+       /* usbdev device-node */
        if (imajor(inode) == USB_DEVICE_MAJOR)
-               dev = usbdev_lookup_minor(iminor(inode));
+               dev = usbdev_lookup_by_minor(iminor(inode));
+#ifdef CONFIG_USB_DEVICEFS
+       /* procfs file */
        if (!dev)
                dev = inode->i_private;
+#endif
        if (!dev)
                goto out;
-       ret = usb_autoresume_device(dev, 1);
+       ret = usb_autoresume_device(dev);
        if (ret)
                goto out;
 
@@ -569,17 +569,18 @@ static int usbdev_open(struct inode *inode, struct file *file)
        ps->dev = dev;
        ps->file = file;
        spin_lock_init(&ps->lock);
+       INIT_LIST_HEAD(&ps->list);
        INIT_LIST_HEAD(&ps->async_pending);
        INIT_LIST_HEAD(&ps->async_completed);
        init_waitqueue_head(&ps->wait);
        ps->discsignr = 0;
-       ps->disc_pid = current->pid;
+       ps->disc_pid = get_pid(task_pid(current));
        ps->disc_uid = current->uid;
        ps->disc_euid = current->euid;
        ps->disccontext = NULL;
        ps->ifclaimed = 0;
        security_task_getsecid(current, &ps->secid);
-       wmb();
+       smp_wmb();
        list_add_tail(&ps->list, &dev->filelist);
        file->private_data = ps;
  out:
@@ -608,9 +609,10 @@ static int usbdev_release(struct inode *inode, struct file *file)
                        releaseintf(ps, ifnum);
        }
        destroy_all_async(ps);
-       usb_autosuspend_device(dev, 1);
+       usb_autosuspend_device(dev);
        usb_unlock_device(dev);
        usb_put_dev(dev);
+       put_pid(ps->disc_pid);
        kfree(ps);
        return 0;
 }
@@ -810,7 +812,6 @@ static int proc_getdriver(struct dev_state *ps, void __user *arg)
 
        if (copy_from_user(&gd, arg, sizeof(gd)))
                return -EFAULT;
-       down_read(&usb_bus_type.subsys.rwsem);
        intf = usb_ifnum_to_if(ps->dev, gd.interface);
        if (!intf || !intf->dev.driver)
                ret = -ENODATA;
@@ -819,7 +820,6 @@ static int proc_getdriver(struct dev_state *ps, void __user *arg)
                                sizeof(gd.driver));
                ret = (copy_to_user(arg, &gd, sizeof(gd)) ? -EFAULT : 0);
        }
-       up_read(&usb_bus_type.subsys.rwsem);
        return ret;
 }
 
@@ -854,11 +854,11 @@ static int proc_setintf(struct dev_state *ps, void __user *arg)
 
 static int proc_setconfig(struct dev_state *ps, void __user *arg)
 {
-       unsigned int u;
+       int u;
        int status = 0;
        struct usb_host_config *actconfig;
 
-       if (get_user(u, (unsigned int __user *)arg))
+       if (get_user(u, (int __user *)arg))
                return -EFAULT;
 
        actconfig = ps->dev->actconfig;
@@ -909,7 +909,7 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,
        struct async *as;
        struct usb_ctrlrequest *dr = NULL;
        unsigned int u, totlen, isofrmlen;
-       int ret, interval = 0, ifnum = -1;
+       int ret, ifnum = -1;
 
        if (uurb->flags & ~(USBDEVFS_URB_ISO_ASAP|USBDEVFS_URB_SHORT_NOT_OK|
                           URB_NO_FSBR|URB_ZERO_PACKET))
@@ -960,7 +960,11 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,
                        kfree(dr);
                        return -EFAULT;
                }
-               snoop(&ps->dev->dev, "control urb\n");
+               snoop(&ps->dev->dev, "control urb: bRequest=%02x "
+                       "bRrequestType=%02x wValue=%04x "
+                       "wIndex=%04x wLength=%04x\n",
+                       dr->bRequest, dr->bRequestType, dr->wValue,
+                       dr->wIndex, dr->wLength);
                break;
 
        case USBDEVFS_URB_TYPE_BULK:
@@ -985,7 +989,6 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,
                if ((ep->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
                                != USB_ENDPOINT_XFER_ISOC)
                        return -EINVAL;
-               interval = 1 << min (15, ep->desc.bInterval - 1);
                isofrmlen = sizeof(struct usbdevfs_iso_packet_desc) * uurb->number_of_packets;
                if (!(isopkt = kmalloc(isofrmlen, GFP_KERNEL)))
                        return -ENOMEM;
@@ -1014,10 +1017,6 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,
                if ((ep->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
                                != USB_ENDPOINT_XFER_INT)
                        return -EINVAL;
-               if (ps->dev->speed == USB_SPEED_HIGH)
-                       interval = 1 << min (15, ep->desc.bInterval - 1);
-               else
-                       interval = ep->desc.bInterval;
                if (uurb->buffer_length > MAX_USBFS_BUFFER_SIZE)
                        return -EINVAL;
                if (!access_ok((uurb->endpoint & USB_DIR_IN) ? VERIFY_WRITE : VERIFY_READ, uurb->buffer, uurb->buffer_length))
@@ -1046,7 +1045,11 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,
        as->urb->setup_packet = (unsigned char*)dr;
        as->urb->start_frame = uurb->start_frame;
        as->urb->number_of_packets = uurb->number_of_packets;
-       as->urb->interval = interval;
+       if (uurb->type == USBDEVFS_URB_TYPE_ISO ||
+                       ps->dev->speed == USB_SPEED_HIGH)
+               as->urb->interval = 1 << min(15, ep->desc.bInterval - 1);
+       else
+               as->urb->interval = ep->desc.bInterval;
         as->urb->context = as;
         as->urb->complete = async_completed;
        for (totlen = u = 0; u < uurb->number_of_packets; u++) {
@@ -1063,7 +1066,7 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,
                as->userbuffer = NULL;
        as->signr = uurb->signr;
        as->ifnum = ifnum;
-       as->pid = current->pid;
+       as->pid = get_pid(task_pid(current));
        as->uid = current->uid;
        as->euid = current->euid;
        security_task_getsecid(current, &as->secid);
@@ -1214,7 +1217,7 @@ static int proc_submiturb_compat(struct dev_state *ps, void __user *arg)
 {
        struct usbdevfs_urb uurb;
 
-       if (get_urb32(&uurb,(struct usbdevfs_urb32 *)arg))
+       if (get_urb32(&uurb,(struct usbdevfs_urb32 __user *)arg))
                return -EFAULT;
 
        return proc_do_submiturb(ps, &uurb, ((struct usbdevfs_urb32 __user *)arg)->iso_frame_desc, arg);
@@ -1249,7 +1252,7 @@ static int processcompl_compat(struct async *as, void __user * __user *arg)
        }
 
        free_async(as);
-       if (put_user((u32)(u64)addr, (u32 __user *)arg))
+       if (put_user(ptr_to_compat(addr), (u32 __user *)arg))
                return -EFAULT;
        return 0;
 }
@@ -1345,15 +1348,12 @@ static int proc_ioctl(struct dev_state *ps, struct usbdevfs_ioctl *ctl)
 
        /* disconnect kernel driver from interface */
        case USBDEVFS_DISCONNECT:
-
-               down_write(&usb_bus_type.subsys.rwsem);
                if (intf->dev.driver) {
                        driver = to_usb_driver(intf->dev.driver);
                        dev_dbg (&intf->dev, "disconnect by usbfs\n");
                        usb_driver_release_interface(driver, intf);
                } else
                        retval = -ENODATA;
-               up_write(&usb_bus_type.subsys.rwsem);
                break;
 
        /* let kernel drivers try to (re)bind to the interface */
@@ -1365,7 +1365,6 @@ static int proc_ioctl(struct dev_state *ps, struct usbdevfs_ioctl *ctl)
 
        /* talk directly to the interface's driver */
        default:
-               down_read(&usb_bus_type.subsys.rwsem);
                if (intf->dev.driver)
                        driver = to_usb_driver(intf->dev.driver);
                if (driver == NULL || driver->ioctl == NULL) {
@@ -1375,7 +1374,6 @@ static int proc_ioctl(struct dev_state *ps, struct usbdevfs_ioctl *ctl)
                        if (retval == -ENOIOCTLCMD)
                                retval = -ENOTTY;
                }
-               up_read(&usb_bus_type.subsys.rwsem);
        }
 
        /* cleanup and return */
@@ -1518,7 +1516,7 @@ static int usbdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd
 
        case USBDEVFS_IOCTL32:
                snoop(&dev->dev, "%s: IOCTL\n", __FUNCTION__);
-               ret = proc_ioctl_compat(ps, (compat_uptr_t)(long)p);
+               ret = proc_ioctl_compat(ps, ptr_to_compat(p));
                break;
 #endif
 
@@ -1577,7 +1575,7 @@ static unsigned int usbdev_poll(struct file *file, struct poll_table_struct *wai
        return mask;
 }
 
-const struct file_operations usbfs_device_file_operations = {
+const struct file_operations usbdev_file_operations = {
        .llseek =       usbdev_lseek,
        .read =         usbdev_read,
        .poll =         usbdev_poll,
@@ -1586,46 +1584,53 @@ const struct file_operations usbfs_device_file_operations = {
        .release =      usbdev_release,
 };
 
-static void usbdev_add(struct usb_device *dev)
+#ifdef CONFIG_USB_DEVICE_CLASS
+static struct class *usb_classdev_class;
+
+static int usb_classdev_add(struct usb_device *dev)
 {
        int minor = ((dev->bus->busnum-1) * 128) + (dev->devnum-1);
 
-       dev->class_dev = class_device_create(usb_device_class, NULL,
-                               MKDEV(USB_DEVICE_MAJOR, minor), &dev->dev,
+       dev->usb_classdev = device_create(usb_classdev_class, &dev->dev,
+                               MKDEV(USB_DEVICE_MAJOR, minor),
                                "usbdev%d.%d", dev->bus->busnum, dev->devnum);
+       if (IS_ERR(dev->usb_classdev))
+               return PTR_ERR(dev->usb_classdev);
 
-       dev->class_dev->class_data = dev;
+       return 0;
 }
 
-static void usbdev_remove(struct usb_device *dev)
+static void usb_classdev_remove(struct usb_device *dev)
 {
-       class_device_unregister(dev->class_dev);
+       device_unregister(dev->usb_classdev);
 }
 
-static int usbdev_notify(struct notifier_block *self, unsigned long action,
-                        void *dev)
+static int usb_classdev_notify(struct notifier_block *self,
+                              unsigned long action, void *dev)
 {
        switch (action) {
        case USB_DEVICE_ADD:
-               usbdev_add(dev);
+               if (usb_classdev_add(dev))
+                       return NOTIFY_BAD;
                break;
        case USB_DEVICE_REMOVE:
-               usbdev_remove(dev);
+               usb_classdev_remove(dev);
                break;
        }
        return NOTIFY_OK;
 }
 
 static struct notifier_block usbdev_nb = {
-       .notifier_call =        usbdev_notify,
+       .notifier_call =        usb_classdev_notify,
 };
+#endif
 
 static struct cdev usb_device_cdev = {
        .kobj   = {.name = "usb_device", },
        .owner  = THIS_MODULE,
 };
 
-int __init usbdev_init(void)
+int __init usb_devio_init(void)
 {
        int retval;
 
@@ -1635,38 +1640,38 @@ int __init usbdev_init(void)
                err("unable to register minors for usb_device");
                goto out;
        }
-       cdev_init(&usb_device_cdev, &usbfs_device_file_operations);
+       cdev_init(&usb_device_cdev, &usbdev_file_operations);
        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);
                goto error_cdev;
        }
-       usb_device_class = class_create(THIS_MODULE, "usb_device");
-       if (IS_ERR(usb_device_class)) {
+#ifdef CONFIG_USB_DEVICE_CLASS
+       usb_classdev_class = class_create(THIS_MODULE, "usb_device");
+       if (IS_ERR(usb_classdev_class)) {
                err("unable to register usb_device class");
-               retval = PTR_ERR(usb_device_class);
-               goto error_class;
+               retval = PTR_ERR(usb_classdev_class);
+               cdev_del(&usb_device_cdev);
+               usb_classdev_class = NULL;
+               goto out;
        }
 
        usb_register_notify(&usbdev_nb);
-
+#endif
 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)
+void usb_devio_cleanup(void)
 {
+#ifdef CONFIG_USB_DEVICE_CLASS
        usb_unregister_notify(&usbdev_nb);
-       class_destroy(usb_device_class);
+       class_destroy(usb_classdev_class);
+#endif
        cdev_del(&usb_device_cdev);
        unregister_chrdev_region(USB_DEVICE_DEV, USB_DEVICE_MAX);
 }
-