]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/usb/core/hcd.c
Merge git://git.infradead.org/mtd-2.6
[linux-2.6-omap-h63xx.git] / drivers / usb / core / hcd.c
index 8ab389dca2b91c49bfdb75e900a1c5a5dde3d334..e1b42626d04d6c7cdd200d86cd8b0d28a6af47ec 100644 (file)
 
 /*-------------------------------------------------------------------------*/
 
+/* Keep track of which host controller drivers are loaded */
+unsigned long usb_hcds_loaded;
+EXPORT_SYMBOL_GPL(usb_hcds_loaded);
+
 /* host controllers we manage */
 LIST_HEAD (usb_bus_list);
 EXPORT_SYMBOL_GPL (usb_bus_list);
@@ -102,6 +106,9 @@ static DEFINE_SPINLOCK(hcd_root_hub_lock);
 /* used when updating an endpoint's URB list */
 static DEFINE_SPINLOCK(hcd_urb_list_lock);
 
+/* used to protect against unlinking URBs after the device is gone */
+static DEFINE_SPINLOCK(hcd_urb_unlink_lock);
+
 /* wait queue for synchronous unlinks */
 DECLARE_WAIT_QUEUE_HEAD(usb_kill_urb_queue);
 
@@ -818,9 +825,8 @@ static int usb_register_bus(struct usb_bus *bus)
        set_bit (busnum, busmap.busmap);
        bus->busnum = busnum;
 
-       bus->dev = device_create_drvdata(usb_host_class, bus->controller,
-                                        MKDEV(0, 0), bus,
-                                        "usb_host%d", busnum);
+       bus->dev = device_create(usb_host_class, bus->controller, MKDEV(0, 0),
+                                bus, "usb_host%d", busnum);
        result = PTR_ERR(bus->dev);
        if (IS_ERR(bus->dev))
                goto error_create_class_dev;
@@ -1373,10 +1379,25 @@ static int unlink1(struct usb_hcd *hcd, struct urb *urb, int status)
 int usb_hcd_unlink_urb (struct urb *urb, int status)
 {
        struct usb_hcd          *hcd;
-       int                     retval;
+       int                     retval = -EIDRM;
+       unsigned long           flags;
 
-       hcd = bus_to_hcd(urb->dev->bus);
-       retval = unlink1(hcd, urb, status);
+       /* Prevent the device and bus from going away while
+        * the unlink is carried out.  If they are already gone
+        * then urb->use_count must be 0, since disconnected
+        * devices can't have any active URBs.
+        */
+       spin_lock_irqsave(&hcd_urb_unlink_lock, flags);
+       if (atomic_read(&urb->use_count) > 0) {
+               retval = 0;
+               usb_get_dev(urb->dev);
+       }
+       spin_unlock_irqrestore(&hcd_urb_unlink_lock, flags);
+       if (retval == 0) {
+               hcd = bus_to_hcd(urb->dev->bus);
+               retval = unlink1(hcd, urb, status);
+               usb_put_dev(urb->dev);
+       }
 
        if (retval == 0)
                retval = -EINPROGRESS;
@@ -1525,6 +1546,17 @@ void usb_hcd_disable_endpoint(struct usb_device *udev,
                hcd->driver->endpoint_disable(hcd, ep);
 }
 
+/* Protect against drivers that try to unlink URBs after the device
+ * is gone, by waiting until all unlinks for @udev are finished.
+ * Since we don't currently track URBs by device, simply wait until
+ * nothing is running in the locked region of usb_hcd_unlink_urb().
+ */
+void usb_hcd_synchronize_unlinks(struct usb_device *udev)
+{
+       spin_lock_irq(&hcd_urb_unlink_lock);
+       spin_unlock_irq(&hcd_urb_unlink_lock);
+}
+
 /*-------------------------------------------------------------------------*/
 
 /* called in any context */