Fedora kernel-2.6.17-1.2142_FC4 patched with stable patch-2.6.17.4-vs2.0.2-rc26.diff
[linux-2.6.git] / drivers / usb / core / usb.c
index 25cf7e9..b7fdc1c 100644 (file)
  */
 
 #include <linux/config.h>
-
-#ifdef CONFIG_USB_DEBUG
-       #define DEBUG
-#else
-       #undef DEBUG
-#endif
-
 #include <linux/module.h>
 #include <linux/string.h>
 #include <linux/bitops.h>
@@ -39,8 +32,8 @@
 #include <linux/spinlock.h>
 #include <linux/errno.h>
 #include <linux/smp_lock.h>
-#include <linux/rwsem.h>
 #include <linux/usb.h>
+#include <linux/mutex.h>
 
 #include <asm/io.h>
 #include <asm/scatterlist.h>
 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);
-
-
-static int generic_probe (struct device *dev)
-{
-       return 0;
-}
-static int generic_remove (struct device *dev)
-{
-       return 0;
-}
-
-static struct device_driver usb_generic_driver = {
-       .owner = THIS_MODULE,
-       .name = "usb",
-       .bus = &usb_bus_type,
-       .probe = generic_probe,
-       .remove = generic_remove,
-};
-
-static int usb_generic_driver_data;
-
-/* called from driver core with usb_bus_type.subsys writelock */
-static int usb_probe_interface(struct device *dev)
-{
-       struct usb_interface * intf = to_usb_interface(dev);
-       struct usb_driver * driver = to_usb_driver(dev->driver);
-       const struct usb_device_id *id;
-       int error = -ENODEV;
-
-       dev_dbg(dev, "%s\n", __FUNCTION__);
-
-       if (!driver->probe)
-               return error;
-       /* FIXME we'd much prefer to just resume it ... */
-       if (interface_to_usbdev(intf)->state == USB_STATE_SUSPENDED)
-               return -EHOSTUNREACH;
-
-       id = usb_match_id (intf, driver->id_table);
-       if (id) {
-               dev_dbg (dev, "%s - got id\n", __FUNCTION__);
-               intf->condition = USB_INTERFACE_BINDING;
-               error = driver->probe (intf, id);
-               intf->condition = error ? USB_INTERFACE_UNBOUND :
-                               USB_INTERFACE_BOUND;
-       }
-
-       return error;
-}
-
-/* called from driver core with usb_bus_type.subsys writelock */
-static int usb_unbind_interface(struct device *dev)
-{
-       struct usb_interface *intf = to_usb_interface(dev);
-       struct usb_driver *driver = to_usb_driver(intf->dev.driver);
 
-       intf->condition = USB_INTERFACE_UNBINDING;
-
-       /* release all urbs for this interface */
-       usb_disable_interface(interface_to_usbdev(intf), intf);
-
-       if (driver && driver->disconnect)
-               driver->disconnect(intf);
-
-       /* reset other interface state */
-       usb_set_interface(interface_to_usbdev(intf),
-                       intf->altsetting[0].desc.bInterfaceNumber,
-                       0);
-       usb_set_intfdata(intf, NULL);
-       intf->condition = USB_INTERFACE_UNBOUND;
-
-       return 0;
-}
-
-/**
- * usb_register - register a USB driver
- * @new_driver: USB operations for the driver
- *
- * Registers a USB driver with the USB core.  The list of unattached
- * interfaces will be rescanned whenever a new driver is added, allowing
- * the new driver to attach to any recognized devices.
- * Returns a negative error code on failure and 0 on success.
- * 
- * NOTE: if you want your driver to use the USB major number, you must call
- * usb_register_dev() to enable that functionality.  This function no longer
- * takes care of that.
- */
-int usb_register(struct usb_driver *new_driver)
-{
-       int retval = 0;
-
-       if (nousb)
-               return -ENODEV;
-
-       new_driver->driver.name = (char *)new_driver->name;
-       new_driver->driver.bus = &usb_bus_type;
-       new_driver->driver.probe = usb_probe_interface;
-       new_driver->driver.remove = usb_unbind_interface;
-       new_driver->driver.owner = new_driver->owner;
-
-       usb_lock_all_devices();
-       retval = driver_register(&new_driver->driver);
-       usb_unlock_all_devices();
-
-       if (!retval) {
-               pr_info("%s: registered new driver %s\n",
-                       usbcore_name, new_driver->name);
-               usbfs_update_special();
-       } else {
-               printk(KERN_ERR "%s: error %d registering driver %s\n",
-                       usbcore_name, retval, new_driver->name);
-       }
-
-       return retval;
-}
-
-/**
- * usb_deregister - unregister a USB driver
- * @driver: USB operations of the driver to unregister
- * Context: must be able to sleep
- *
- * Unlinks the specified driver from the internal USB driver list.
- * 
- * NOTE: If you called usb_register_dev(), you still need to call
- * usb_deregister_dev() to clean up your driver's allocated minor numbers,
- * this * call will no longer do it for you.
- */
-void usb_deregister(struct usb_driver *driver)
-{
-       pr_info("%s: deregistering driver %s\n", usbcore_name, driver->name);
-
-       usb_lock_all_devices();
-       driver_unregister (&driver->driver);
-       usb_unlock_all_devices();
-
-       usbfs_update_special();
-}
 
 /**
  * usb_ifnum_to_if - get the interface object with a given interface number
@@ -289,11 +144,12 @@ int usb_driver_claim_interface(struct usb_driver *driver,
        dev->driver = &driver->driver;
        usb_set_intfdata(iface, priv);
        iface->condition = USB_INTERFACE_BOUND;
+       mark_active(iface);
 
        /* if interface was already added, bind now; else let
         * the future device_add() bind it, bypassing probe()
         */
-       if (!list_empty (&dev->bus_list))
+       if (device_is_registered(dev))
                device_bind_driver(dev);
 
        return 0;
@@ -322,144 +178,42 @@ void usb_driver_release_interface(struct usb_driver *driver,
        if (!dev->driver || dev->driver != &driver->driver)
                return;
 
-       /* don't disconnect from disconnect(), or before dev_add() */
-       if (!list_empty (&dev->driver_list) && !list_empty (&dev->bus_list))
+       /* don't release from within disconnect() */
+       if (iface->condition != USB_INTERFACE_BOUND)
+               return;
+
+       /* don't release if the interface hasn't been added yet */
+       if (device_is_registered(dev)) {
+               iface->condition = USB_INTERFACE_UNBINDING;
                device_release_driver(dev);
+       }
 
        dev->driver = NULL;
        usb_set_intfdata(iface, NULL);
        iface->condition = USB_INTERFACE_UNBOUND;
+       mark_quiesced(iface);
 }
 
-/**
- * usb_match_id - find first usb_device_id matching device or interface
- * @interface: the interface of interest
- * @id: array of usb_device_id structures, terminated by zero entry
- *
- * usb_match_id searches an array of usb_device_id's and returns
- * the first one matching the device or interface, or null.
- * This is used when binding (or rebinding) a driver to an interface.
- * Most USB device drivers will use this indirectly, through the usb core,
- * but some layered driver frameworks use it directly.
- * These device tables are exported with MODULE_DEVICE_TABLE, through
- * modutils and "modules.usbmap", to support the driver loading
- * functionality of USB hotplugging.
- *
- * What Matches:
- *
- * The "match_flags" element in a usb_device_id controls which
- * members are used.  If the corresponding bit is set, the
- * value in the device_id must match its corresponding member
- * in the device or interface descriptor, or else the device_id
- * does not match.
- *
- * "driver_info" is normally used only by device drivers,
- * but you can create a wildcard "matches anything" usb_device_id
- * as a driver's "modules.usbmap" entry if you provide an id with
- * only a nonzero "driver_info" field.  If you do this, the USB device
- * driver's probe() routine should use additional intelligence to
- * decide whether to bind to the specified interface.
- * 
- * What Makes Good usb_device_id Tables:
- *
- * The match algorithm is very simple, so that intelligence in
- * driver selection must come from smart driver id records.
- * Unless you have good reasons to use another selection policy,
- * provide match elements only in related groups, and order match
- * specifiers from specific to general.  Use the macros provided
- * for that purpose if you can.
- *
- * The most specific match specifiers use device descriptor
- * data.  These are commonly used with product-specific matches;
- * the USB_DEVICE macro lets you provide vendor and product IDs,
- * and you can also match against ranges of product revisions.
- * These are widely used for devices with application or vendor
- * specific bDeviceClass values.
- *
- * Matches based on device class/subclass/protocol specifications
- * are slightly more general; use the USB_DEVICE_INFO macro, or
- * its siblings.  These are used with single-function devices
- * where bDeviceClass doesn't specify that each interface has
- * its own class. 
- *
- * Matches based on interface class/subclass/protocol are the
- * most general; they let drivers bind to any interface on a
- * multiple-function device.  Use the USB_INTERFACE_INFO
- * macro, or its siblings, to match class-per-interface style 
- * devices (as recorded in bDeviceClass).
- *  
- * Within those groups, remember that not all combinations are
- * meaningful.  For example, don't give a product version range
- * without vendor and product IDs; or specify a protocol without
- * its associated class and subclass.
- */   
-const struct usb_device_id *
-usb_match_id(struct usb_interface *interface, const struct usb_device_id *id)
-{
-       struct usb_host_interface *intf;
-       struct usb_device *dev;
-
-       /* proc_connectinfo in devio.c may call us with id == NULL. */
-       if (id == NULL)
-               return NULL;
-
-       intf = interface->cur_altsetting;
-       dev = interface_to_usbdev(interface);
-
-       /* It is important to check that id->driver_info is nonzero,
-          since an entry that is all zeroes except for a nonzero
-          id->driver_info is the way to create an entry that
-          indicates that the driver want to examine every
-          device and interface. */
-       for (; id->idVendor || id->bDeviceClass || id->bInterfaceClass ||
-              id->driver_info; id++) {
-
-               if ((id->match_flags & USB_DEVICE_ID_MATCH_VENDOR) &&
-                   id->idVendor != le16_to_cpu(dev->descriptor.idVendor))
-                       continue;
-
-               if ((id->match_flags & USB_DEVICE_ID_MATCH_PRODUCT) &&
-                   id->idProduct != le16_to_cpu(dev->descriptor.idProduct))
-                       continue;
-
-               /* No need to test id->bcdDevice_lo != 0, since 0 is never
-                  greater than any unsigned number. */
-               if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_LO) &&
-                   (id->bcdDevice_lo > le16_to_cpu(dev->descriptor.bcdDevice)))
-                       continue;
-
-               if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_HI) &&
-                   (id->bcdDevice_hi < le16_to_cpu(dev->descriptor.bcdDevice)))
-                       continue;
-
-               if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_CLASS) &&
-                   (id->bDeviceClass != dev->descriptor.bDeviceClass))
-                       continue;
-
-               if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_SUBCLASS) &&
-                   (id->bDeviceSubClass!= dev->descriptor.bDeviceSubClass))
-                       continue;
-
-               if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_PROTOCOL) &&
-                   (id->bDeviceProtocol != dev->descriptor.bDeviceProtocol))
-                       continue;
-
-               if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_CLASS) &&
-                   (id->bInterfaceClass != intf->desc.bInterfaceClass))
-                       continue;
+struct find_interface_arg {
+       int minor;
+       struct usb_interface *interface;
+};
 
-               if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_SUBCLASS) &&
-                   (id->bInterfaceSubClass != intf->desc.bInterfaceSubClass))
-                       continue;
+static int __find_interface(struct device * dev, void * data)
+{
+       struct find_interface_arg *arg = data;
+       struct usb_interface *intf;
 
-               if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_PROTOCOL) &&
-                   (id->bInterfaceProtocol != intf->desc.bInterfaceProtocol))
-                       continue;
+       /* can't look at usb devices, only interfaces */
+       if (dev->driver == &usb_generic_driver)
+               return 0;
 
-               return id;
+       intf = to_usb_interface(dev);
+       if (intf->minor != -1 && intf->minor == arg->minor) {
+               arg->interface = intf;
+               return 1;
        }
-
-       return NULL;
+       return 0;
 }
 
 /**
@@ -473,56 +227,18 @@ usb_match_id(struct usb_interface *interface, const struct usb_device_id *id)
  */
 struct usb_interface *usb_find_interface(struct usb_driver *drv, int minor)
 {
-       struct list_head *entry;
-       struct device *dev;
-       struct usb_interface *intf;
-
-       list_for_each(entry, &drv->driver.devices) {
-               dev = container_of(entry, struct device, driver_list);
-
-               /* can't look at usb devices, only interfaces */
-               if (dev->driver == &usb_generic_driver)
-                       continue;
-
-               intf = to_usb_interface(dev);
-               if (intf->minor == -1)
-                       continue;
-               if (intf->minor == minor)
-                       return intf;
-       }
-
-       /* no device found that matches */
-       return NULL;    
-}
-
-static int usb_device_match (struct device *dev, struct device_driver *drv)
-{
-       struct usb_interface *intf;
-       struct usb_driver *usb_drv;
-       const struct usb_device_id *id;
-
-       /* check for generic driver, which we don't match any device with */
-       if (drv == &usb_generic_driver)
-               return 0;
-
-       intf = to_usb_interface(dev);
-       usb_drv = to_usb_driver(drv);
-       
-       id = usb_match_id (intf, usb_drv->id_table);
-       if (id)
-               return 1;
+       struct find_interface_arg argb;
 
-       return 0;
+       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.
  *
@@ -531,11 +247,12 @@ static int usb_device_match (struct device *dev, struct device_driver *drv)
  * 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;
+       struct usb_host_interface *alt;
        int i = 0;
        int length = 0;
 
@@ -543,7 +260,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) || 
@@ -552,7 +269,8 @@ static int usb_hotplug (struct device *dev, char **envp, int num_envp,
 
        intf = to_usb_interface(dev);
        usb_dev = interface_to_usbdev (intf);
-       
+       alt = intf->cur_altsetting;
+
        if (usb_dev->devnum < 0) {
                pr_debug ("usb %s: already deleted?\n", dev->bus_id);
                return -ENODEV;
@@ -569,71 +287,52 @@ 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 (usb_dev->descriptor.bDeviceClass == 0) {
-               struct usb_host_interface *alt = intf->cur_altsetting;
-
-               /* 2.4 only exposed interface zero.  in 2.5, hotplug
-                * agents are called for all interfaces, and can use
-                * $DEVPATH/bInterfaceNumber if necessary.
-                */
-               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))
-                       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))
-                       return -ENOMEM;
-       } else {
-               if (add_hotplug_env_var(envp, num_envp, &i,
-                                       buffer, buffer_size, &length,
-                                       "MODALIAS=usb:v%04Xp%04Xd%04Xdc%02Xdsc%02Xdp%02Xic*isc*ip*",
-                                       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))
-                       return -ENOMEM;
-       }
+       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_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;
 
@@ -642,7 +341,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;
@@ -688,12 +387,10 @@ usb_alloc_dev(struct usb_device *parent, struct usb_bus *bus, unsigned port1)
 {
        struct usb_device *dev;
 
-       dev = kmalloc(sizeof(*dev), GFP_KERNEL);
+       dev = kzalloc(sizeof(*dev), GFP_KERNEL);
        if (!dev)
                return NULL;
 
-       memset(dev, 0, sizeof(*dev));
-
        bus = usb_bus_get(bus);
        if (!bus) {
                kfree(dev);
@@ -743,12 +440,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;
 }
 
@@ -821,75 +517,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
@@ -901,7 +542,7 @@ int usb_trylock_device(struct usb_device *udev)
  * is neither BINDING nor BOUND.  Rather than sleeping to wait for the
  * lock, the routine polls repeatedly.  This is to prevent deadlock with
  * disconnect; in some drivers (such as usb-storage) the disconnect()
- * callback will block waiting for a device reset to complete.
+ * or suspend() method will block waiting for a device reset to complete.
  *
  * Returns a negative error code for failure, otherwise 1 or 0 to indicate
  * that the device will or will not have to be unlocked.  (0 can be
@@ -911,6 +552,8 @@ int usb_trylock_device(struct usb_device *udev)
 int usb_lock_device_for_reset(struct usb_device *udev,
                struct usb_interface *iface)
 {
+       unsigned long jiffies_expire = jiffies + HZ;
+
        if (udev->state == USB_STATE_NOTATTACHED)
                return -ENODEV;
        if (udev->state == USB_STATE_SUSPENDED)
@@ -926,7 +569,13 @@ 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 */
+               if (time_after(jiffies, jiffies_expire))
+                       return -EBUSY;
+
                msleep(15);
                if (udev->state == USB_STATE_NOTATTACHED)
                        return -ENODEV;
@@ -938,39 +587,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)
@@ -993,10 +609,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;
                }
@@ -1024,7 +640,7 @@ struct usb_device *usb_find_device(u16 vendor_id, u16 product_id)
        struct usb_bus *bus;
        struct usb_device *dev = NULL;
        
-       down(&usb_bus_list_lock);
+       mutex_lock(&usb_bus_list_lock);
        for (buslist = usb_bus_list.next;
             buslist != &usb_bus_list; 
             buslist = buslist->next) {
@@ -1038,7 +654,7 @@ struct usb_device *usb_find_device(u16 vendor_id, u16 product_id)
                        goto exit;
        }
 exit:
-       up(&usb_bus_list_lock);
+       mutex_unlock(&usb_bus_list_lock);
        return dev;
 }
 
@@ -1118,7 +734,7 @@ int __usb_get_extra_descriptor(char *buffer, unsigned size,
 void *usb_buffer_alloc (
        struct usb_device *dev,
        size_t size,
-       int mem_flags,
+       gfp_t mem_flags,
        dma_addr_t *dma
 )
 {
@@ -1373,13 +989,30 @@ void usb_buffer_unmap_sg (struct usb_device *dev, unsigned pipe,
                        usb_pipein (pipe) ? DMA_FROM_DEVICE : DMA_TO_DEVICE);
 }
 
+static int verify_suspended(struct device *dev, void *unused)
+{
+       return (dev->power.power_state.event == PM_EVENT_ON) ? -EBUSY : 0;
+}
+
 static int usb_generic_suspend(struct device *dev, pm_message_t message)
 {
-       struct usb_interface *intf;
-       struct usb_driver *driver;
+       struct usb_interface    *intf;
+       struct usb_driver       *driver;
+       int                     status;
 
-       if (dev->driver == &usb_generic_driver)
-               return usb_suspend_device (to_usb_device(dev), message);
+       /* USB devices enter SUSPEND state through their hubs, but can be
+        * marked for FREEZE as soon as their children are already idled.
+        * But those semantics are useless, so we equate the two (sigh).
+        */
+       if (dev->driver == &usb_generic_driver) {
+               if (dev->power.power_state.event == message.event)
+                       return 0;
+               /* we need to rule out bogus requests through sysfs */
+               status = device_for_each_child(dev, NULL, verify_suspended);
+               if (status)
+                       return status;
+               return usb_suspend_device (to_usb_device(dev));
+       }
 
        if ((dev->driver == NULL) ||
            (dev->driver_data == &usb_generic_driver_data))
@@ -1388,56 +1021,84 @@ static int usb_generic_suspend(struct device *dev, pm_message_t message)
        intf = to_usb_interface(dev);
        driver = to_usb_driver(dev->driver);
 
-       /* there's only one USB suspend state */
-       if (intf->dev.power.power_state)
+       /* with no hardware, USB interfaces only use FREEZE and ON states */
+       if (!is_active(intf))
                return 0;
 
-       if (driver->suspend)
-               return driver->suspend(intf, message);
-       return 0;
+       if (driver->suspend && driver->resume) {
+               status = driver->suspend(intf, message);
+               if (status)
+                       dev_err(dev, "%s error %d\n", "suspend", status);
+               else
+                       mark_quiesced(intf);
+       } else {
+               // FIXME else if there's no suspend method, disconnect...
+               dev_warn(dev, "no suspend for driver %s?\n", driver->name);
+               mark_quiesced(intf);
+               status = 0;
+       }
+       return status;
 }
 
 static int usb_generic_resume(struct device *dev)
 {
-       struct usb_interface *intf;
-       struct usb_driver *driver;
+       struct usb_interface    *intf;
+       struct usb_driver       *driver;
+       struct usb_device       *udev;
+       int                     status;
 
-       /* devices resume through their hub */
-       if (dev->driver == &usb_generic_driver)
+       if (dev->power.power_state.event == PM_EVENT_ON)
+               return 0;
+
+       /* mark things as "on" immediately, no matter what errors crop up */
+       dev->power.power_state.event = PM_EVENT_ON;
+
+       /* devices resume through their hubs */
+       if (dev->driver == &usb_generic_driver) {
+               udev = to_usb_device(dev);
+               if (udev->state == USB_STATE_NOTATTACHED)
+                       return 0;
                return usb_resume_device (to_usb_device(dev));
+       }
 
        if ((dev->driver == NULL) ||
-           (dev->driver_data == &usb_generic_driver_data))
+           (dev->driver_data == &usb_generic_driver_data)) {
+               dev->power.power_state.event = PM_EVENT_FREEZE;
                return 0;
+       }
 
        intf = to_usb_interface(dev);
        driver = to_usb_driver(dev->driver);
 
-       if (driver->resume)
-               return driver->resume(intf);
+       udev = interface_to_usbdev(intf);
+       if (udev->state == USB_STATE_NOTATTACHED)
+               return 0;
+
+       /* if driver was suspended, it has a resume method;
+        * however, sysfs can wrongly mark things as suspended
+        * (on the "no suspend method" FIXME path above)
+        */
+       if (driver->resume) {
+               status = driver->resume(intf);
+               if (status) {
+                       dev_err(dev, "%s error %d\n", "resume", status);
+                       mark_quiesced(intf);
+               }
+       } else
+               dev_warn(dev, "no resume for driver %s?\n", driver->name);
        return 0;
 }
 
 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>
@@ -1467,13 +1128,18 @@ static int __init usb_init(void)
        retval = usb_major_init();
        if (retval)
                goto major_init_failed;
+       retval = usb_register(&usbfs_driver);
+       if (retval)
+               goto driver_register_failed;
+       retval = usbdev_init();
+       if (retval)
+               goto usbdevice_init_failed;
        retval = usbfs_init();
        if (retval)
                goto fs_init_failed;
        retval = usb_hub_init();
        if (retval)
                goto hub_init_failed;
-
        retval = driver_register(&usb_generic_driver);
        if (!retval)
                goto out;
@@ -1482,7 +1148,11 @@ static int __init usb_init(void)
 hub_init_failed:
        usbfs_cleanup();
 fs_init_failed:
-       usb_major_cleanup();    
+       usbdev_cleanup();
+usbdevice_init_failed:
+       usb_deregister(&usbfs_driver);
+driver_register_failed:
+       usb_major_cleanup();
 major_init_failed:
        usb_host_cleanup();
 host_init_failed:
@@ -1503,6 +1173,8 @@ static void __exit usb_exit(void)
        driver_unregister(&usb_generic_driver);
        usb_major_cleanup();
        usbfs_cleanup();
+       usb_deregister(&usbfs_driver);
+       usbdev_cleanup();
        usb_hub_cleanup();
        usb_host_cleanup();
        bus_unregister(&usb_bus_type);
@@ -1517,29 +1189,24 @@ module_exit(usb_exit);
  * driver modules to use.
  */
 
-EXPORT_SYMBOL(usb_register);
-EXPORT_SYMBOL(usb_deregister);
 EXPORT_SYMBOL(usb_disabled);
 
-EXPORT_SYMBOL(usb_alloc_dev);
+EXPORT_SYMBOL_GPL(usb_get_intf);
+EXPORT_SYMBOL_GPL(usb_put_intf);
+
 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);
-EXPORT_SYMBOL(usb_match_id);
 EXPORT_SYMBOL(usb_find_interface);
 EXPORT_SYMBOL(usb_ifnum_to_if);
 EXPORT_SYMBOL(usb_altnum_to_altsetting);
 
 EXPORT_SYMBOL(usb_reset_device);
-EXPORT_SYMBOL(usb_disconnect);
 
 EXPORT_SYMBOL(__usb_get_extra_descriptor);