Merge rsync://rsync.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6
[firefly-linux-kernel-4.4.55.git] / drivers / usb / core / usb.c
index 294e9f12747794006271b96bcdc3a2e488b1b6a2..13d1d367f7f1ed6e80a386a69bf1fed4d5f4d3de 100644 (file)
@@ -32,7 +32,6 @@
 #include <linux/spinlock.h>
 #include <linux/errno.h>
 #include <linux/smp_lock.h>
-#include <linux/rwsem.h>
 #include <linux/usb.h>
 
 #include <asm/io.h>
@@ -47,9 +46,6 @@
 const char *usbcore_name = "usbcore";
 
 static int nousb;      /* Disable USB when built into kernel image */
-                       /* Not honored on modular build */
-
-static DECLARE_RWSEM(usb_all_devices_rwsem);
 
 
 /**
@@ -197,19 +193,23 @@ void usb_driver_release_interface(struct usb_driver *driver,
        mark_quiesced(iface);
 }
 
+struct find_interface_arg {
+       int minor;
+       struct usb_interface *interface;
+};
+
 static int __find_interface(struct device * dev, void * data)
 {
-       struct usb_interface ** ret = (struct usb_interface **)data;
-       struct usb_interface * intf = *ret;
-       int *minor = (int *)data;
+       struct find_interface_arg *arg = data;
+       struct usb_interface *intf;
 
        /* can't look at usb devices, only interfaces */
        if (dev->driver == &usb_generic_driver)
                return 0;
 
        intf = to_usb_interface(dev);
-       if (intf->minor != -1 && intf->minor == *minor) {
-               *ret = intf;
+       if (intf->minor != -1 && intf->minor == arg->minor) {
+               arg->interface = intf;
                return 1;
        }
        return 0;
@@ -226,21 +226,18 @@ static int __find_interface(struct device * dev, void * data)
  */
 struct usb_interface *usb_find_interface(struct usb_driver *drv, int minor)
 {
-       struct usb_interface *intf = (struct usb_interface *)(long)minor;
-       int ret;
+       struct find_interface_arg argb;
 
-       ret = driver_for_each_device(&drv->driver, NULL, &intf, __find_interface);
-
-       return ret ? intf : NULL;
+       argb.minor = minor;
+       argb.interface = NULL;
+       driver_for_each_device(&drv->driver, NULL, &argb, __find_interface);
+       return argb.interface;
 }
 
 #ifdef CONFIG_HOTPLUG
 
 /*
- * USB hotplugging invokes what /proc/sys/kernel/hotplug says
- * (normally /sbin/hotplug) when USB devices get added or removed.
- *
- * This invokes a user mode policy agent, typically helping to load driver
+ * This sends an uevent to userspace, typically helping to load driver
  * or other modules, configure the device, and more.  Drivers can provide
  * a MODULE_DEVICE_TABLE to help with module loading subtasks.
  *
@@ -249,8 +246,8 @@ struct usb_interface *usb_find_interface(struct usb_driver *drv, int minor)
  * delays in event delivery.  Use sysfs (and DEVPATH) to make sure the
  * device (and this configuration!) are still present.
  */
-static int usb_hotplug (struct device *dev, char **envp, int num_envp,
-                       char *buffer, int buffer_size)
+static int usb_uevent(struct device *dev, char **envp, int num_envp,
+                     char *buffer, int buffer_size)
 {
        struct usb_interface *intf;
        struct usb_device *usb_dev;
@@ -262,7 +259,7 @@ static int usb_hotplug (struct device *dev, char **envp, int num_envp,
                return -ENODEV;
 
        /* driver is often null here; dev_dbg() would oops */
-       pr_debug ("usb %s: hotplug\n", dev->bus_id);
+       pr_debug ("usb %s: uevent\n", dev->bus_id);
 
        /* Must check driver_data here, as on remove driver is always NULL */
        if ((dev->driver == &usb_generic_driver) || 
@@ -289,51 +286,51 @@ static int usb_hotplug (struct device *dev, char **envp, int num_envp,
         *
         * FIXME reduce hardwired intelligence here
         */
-       if (add_hotplug_env_var(envp, num_envp, &i,
-                               buffer, buffer_size, &length,
-                               "DEVICE=/proc/bus/usb/%03d/%03d",
-                               usb_dev->bus->busnum, usb_dev->devnum))
+       if (add_uevent_var(envp, num_envp, &i,
+                          buffer, buffer_size, &length,
+                          "DEVICE=/proc/bus/usb/%03d/%03d",
+                          usb_dev->bus->busnum, usb_dev->devnum))
                return -ENOMEM;
 #endif
 
        /* per-device configurations are common */
-       if (add_hotplug_env_var(envp, num_envp, &i,
-                               buffer, buffer_size, &length,
-                               "PRODUCT=%x/%x/%x",
-                               le16_to_cpu(usb_dev->descriptor.idVendor),
-                               le16_to_cpu(usb_dev->descriptor.idProduct),
-                               le16_to_cpu(usb_dev->descriptor.bcdDevice)))
+       if (add_uevent_var(envp, num_envp, &i,
+                          buffer, buffer_size, &length,
+                          "PRODUCT=%x/%x/%x",
+                          le16_to_cpu(usb_dev->descriptor.idVendor),
+                          le16_to_cpu(usb_dev->descriptor.idProduct),
+                          le16_to_cpu(usb_dev->descriptor.bcdDevice)))
                return -ENOMEM;
 
        /* class-based driver binding models */
-       if (add_hotplug_env_var(envp, num_envp, &i,
-                               buffer, buffer_size, &length,
-                               "TYPE=%d/%d/%d",
-                               usb_dev->descriptor.bDeviceClass,
-                               usb_dev->descriptor.bDeviceSubClass,
-                               usb_dev->descriptor.bDeviceProtocol))
+       if (add_uevent_var(envp, num_envp, &i,
+                          buffer, buffer_size, &length,
+                          "TYPE=%d/%d/%d",
+                          usb_dev->descriptor.bDeviceClass,
+                          usb_dev->descriptor.bDeviceSubClass,
+                          usb_dev->descriptor.bDeviceProtocol))
                return -ENOMEM;
 
-       if (add_hotplug_env_var(envp, num_envp, &i,
-                               buffer, buffer_size, &length,
-                               "INTERFACE=%d/%d/%d",
-                               alt->desc.bInterfaceClass,
-                               alt->desc.bInterfaceSubClass,
-                               alt->desc.bInterfaceProtocol))
+       if (add_uevent_var(envp, num_envp, &i,
+                          buffer, buffer_size, &length,
+                          "INTERFACE=%d/%d/%d",
+                          alt->desc.bInterfaceClass,
+                          alt->desc.bInterfaceSubClass,
+                          alt->desc.bInterfaceProtocol))
                return -ENOMEM;
 
-       if (add_hotplug_env_var(envp, num_envp, &i,
-                               buffer, buffer_size, &length,
-                               "MODALIAS=usb:v%04Xp%04Xd%04Xdc%02Xdsc%02Xdp%02Xic%02Xisc%02Xip%02X",
-                               le16_to_cpu(usb_dev->descriptor.idVendor),
-                               le16_to_cpu(usb_dev->descriptor.idProduct),
-                               le16_to_cpu(usb_dev->descriptor.bcdDevice),
-                               usb_dev->descriptor.bDeviceClass,
-                               usb_dev->descriptor.bDeviceSubClass,
-                               usb_dev->descriptor.bDeviceProtocol,
-                               alt->desc.bInterfaceClass,
-                               alt->desc.bInterfaceSubClass,
-                               alt->desc.bInterfaceProtocol))
+       if (add_uevent_var(envp, num_envp, &i,
+                          buffer, buffer_size, &length,
+                          "MODALIAS=usb:v%04Xp%04Xd%04Xdc%02Xdsc%02Xdp%02Xic%02Xisc%02Xip%02X",
+                          le16_to_cpu(usb_dev->descriptor.idVendor),
+                          le16_to_cpu(usb_dev->descriptor.idProduct),
+                          le16_to_cpu(usb_dev->descriptor.bcdDevice),
+                          usb_dev->descriptor.bDeviceClass,
+                          usb_dev->descriptor.bDeviceSubClass,
+                          usb_dev->descriptor.bDeviceProtocol,
+                          alt->desc.bInterfaceClass,
+                          alt->desc.bInterfaceSubClass,
+                          alt->desc.bInterfaceProtocol))
                return -ENOMEM;
 
        envp[i] = NULL;
@@ -343,7 +340,7 @@ static int usb_hotplug (struct device *dev, char **envp, int num_envp,
 
 #else
 
-static int usb_hotplug (struct device *dev, char **envp,
+static int usb_uevent(struct device *dev, char **envp,
                        int num_envp, char *buffer, int buffer_size)
 {
        return -ENODEV;
@@ -442,12 +439,11 @@ usb_alloc_dev(struct usb_device *parent, struct usb_bus *bus, unsigned port1)
                /* hub driver sets up TT records */
        }
 
+       dev->portnum = port1;
        dev->bus = bus;
        dev->parent = parent;
        INIT_LIST_HEAD(&dev->filelist);
 
-       init_MUTEX(&dev->serialize);
-
        return dev;
 }
 
@@ -520,75 +516,20 @@ void usb_put_intf(struct usb_interface *intf)
 
 /*                     USB device locking
  *
- * Although locking USB devices should be straightforward, it is
- * complicated by the way the driver-model core works.  When a new USB
- * driver is registered or unregistered, the core will automatically
- * probe or disconnect all matching interfaces on all USB devices while
- * holding the USB subsystem writelock.  There's no good way for us to
- * tell which devices will be used or to lock them beforehand; our only
- * option is to effectively lock all the USB devices.
- *
- * We do that by using a private rw-semaphore, usb_all_devices_rwsem.
- * When locking an individual device you must first acquire the rwsem's
- * readlock.  When a driver is registered or unregistered the writelock
- * must be held.  These actions are encapsulated in the subroutines
- * below, so all a driver needs to do is call usb_lock_device() and
- * usb_unlock_device().
+ * USB devices and interfaces are locked using the semaphore in their
+ * embedded struct device.  The hub driver guarantees that whenever a
+ * device is connected or disconnected, drivers are called with the
+ * USB device locked as well as their particular interface.
  *
  * Complications arise when several devices are to be locked at the same
  * time.  Only hub-aware drivers that are part of usbcore ever have to
- * do this; nobody else needs to worry about it.  The problem is that
- * usb_lock_device() must not be called to lock a second device since it
- * would acquire the rwsem's readlock reentrantly, leading to deadlock if
- * another thread was waiting for the writelock.  The solution is simple:
- *
- *     When locking more than one device, call usb_lock_device()
- *     to lock the first one.  Lock the others by calling
- *     down(&udev->serialize) directly.
- *
- *     When unlocking multiple devices, use up(&udev->serialize)
- *     to unlock all but the last one.  Unlock the last one by
- *     calling usb_unlock_device().
+ * do this; nobody else needs to worry about it.  The rule for locking
+ * is simple:
  *
  *     When locking both a device and its parent, always lock the
  *     the parent first.
  */
 
-/**
- * usb_lock_device - acquire the lock for a usb device structure
- * @udev: device that's being locked
- *
- * Use this routine when you don't hold any other device locks;
- * to acquire nested inner locks call down(&udev->serialize) directly.
- * This is necessary for proper interaction with usb_lock_all_devices().
- */
-void usb_lock_device(struct usb_device *udev)
-{
-       down_read(&usb_all_devices_rwsem);
-       down(&udev->serialize);
-}
-
-/**
- * usb_trylock_device - attempt to acquire the lock for a usb device structure
- * @udev: device that's being locked
- *
- * Don't use this routine if you already hold a device lock;
- * use down_trylock(&udev->serialize) instead.
- * This is necessary for proper interaction with usb_lock_all_devices().
- *
- * Returns 1 if successful, 0 if contention.
- */
-int usb_trylock_device(struct usb_device *udev)
-{
-       if (!down_read_trylock(&usb_all_devices_rwsem))
-               return 0;
-       if (down_trylock(&udev->serialize)) {
-               up_read(&usb_all_devices_rwsem);
-               return 0;
-       }
-       return 1;
-}
-
 /**
  * usb_lock_device_for_reset - cautiously acquire the lock for a
  *     usb device structure
@@ -627,7 +568,7 @@ int usb_lock_device_for_reset(struct usb_device *udev,
                }
        }
 
-       while (!usb_trylock_device(udev)) {
+       while (usb_trylock_device(udev) != 0) {
 
                /* If we can't acquire the lock after waiting one second,
                 * we're probably deadlocked */
@@ -645,39 +586,6 @@ int usb_lock_device_for_reset(struct usb_device *udev,
        return 1;
 }
 
-/**
- * usb_unlock_device - release the lock for a usb device structure
- * @udev: device that's being unlocked
- *
- * Use this routine when releasing the only device lock you hold;
- * to release inner nested locks call up(&udev->serialize) directly.
- * This is necessary for proper interaction with usb_lock_all_devices().
- */
-void usb_unlock_device(struct usb_device *udev)
-{
-       up(&udev->serialize);
-       up_read(&usb_all_devices_rwsem);
-}
-
-/**
- * usb_lock_all_devices - acquire the lock for all usb device structures
- *
- * This is necessary when registering a new driver or probing a bus,
- * since the driver-model core may try to use any usb_device.
- */
-void usb_lock_all_devices(void)
-{
-       down_write(&usb_all_devices_rwsem);
-}
-
-/**
- * usb_unlock_all_devices - release the lock for all usb device structures
- */
-void usb_unlock_all_devices(void)
-{
-       up_write(&usb_all_devices_rwsem);
-}
-
 
 static struct usb_device *match_device(struct usb_device *dev,
                                       u16 vendor_id, u16 product_id)
@@ -700,10 +608,10 @@ static struct usb_device *match_device(struct usb_device *dev,
        /* look through all of the children of this device */
        for (child = 0; child < dev->maxchild; ++child) {
                if (dev->children[child]) {
-                       down(&dev->children[child]->serialize);
+                       usb_lock_device(dev->children[child]);
                        ret_dev = match_device(dev->children[child],
                                               vendor_id, product_id);
-                       up(&dev->children[child]->serialize);
+                       usb_unlock_device(dev->children[child]);
                        if (ret_dev)
                                goto exit;
                }
@@ -1183,23 +1091,13 @@ static int usb_generic_resume(struct device *dev)
 struct bus_type usb_bus_type = {
        .name =         "usb",
        .match =        usb_device_match,
-       .hotplug =      usb_hotplug,
+       .uevent =       usb_uevent,
        .suspend =      usb_generic_suspend,
        .resume =       usb_generic_resume,
 };
 
-#ifndef MODULE
-
-static int __init usb_setup_disable(char *str)
-{
-       nousb = 1;
-       return 1;
-}
-
 /* format to disable USB on kernel command line is: nousb */
-__setup("nousb", usb_setup_disable);
-
-#endif
+__module_param_call("", nousb, param_set_bool, param_get_bool, &nousb, 0444);
 
 /*
  * for external read access to <nousb>
@@ -1300,10 +1198,7 @@ EXPORT_SYMBOL(usb_put_dev);
 EXPORT_SYMBOL(usb_get_dev);
 EXPORT_SYMBOL(usb_hub_tt_clear_buffer);
 
-EXPORT_SYMBOL(usb_lock_device);
-EXPORT_SYMBOL(usb_trylock_device);
 EXPORT_SYMBOL(usb_lock_device_for_reset);
-EXPORT_SYMBOL(usb_unlock_device);
 
 EXPORT_SYMBOL(usb_driver_claim_interface);
 EXPORT_SYMBOL(usb_driver_release_interface);