USB: usb_driver_claim_interface: add sanity checking
[pandora-kernel.git] / drivers / usb / core / driver.c
index c2c0ae5..c595714 100644 (file)
@@ -24,6 +24,7 @@
 
 #include <linux/device.h>
 #include <linux/slab.h>
+#include <linux/export.h>
 #include <linux/usb.h>
 #include <linux/usb/quirks.h>
 #include <linux/usb/hcd.h>
@@ -427,9 +428,13 @@ static int usb_unbind_interface(struct device *dev)
 int usb_driver_claim_interface(struct usb_driver *driver,
                                struct usb_interface *iface, void *priv)
 {
-       struct device *dev = &iface->dev;
+       struct device *dev;
        int retval = 0;
 
+       if (!iface)
+               return -ENODEV;
+
+       dev = &iface->dev;
        if (dev->driver)
                return -EBUSY;
 
@@ -540,22 +545,10 @@ int usb_match_device(struct usb_device *dev, const struct usb_device_id *id)
 }
 
 /* returns 0 if no match, 1 if match */
-int usb_match_one_id(struct usb_interface *interface,
-                    const struct usb_device_id *id)
+int usb_match_one_id_intf(struct usb_device *dev,
+                         struct usb_host_interface *intf,
+                         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 0;
-
-       intf = interface->cur_altsetting;
-       dev = interface_to_usbdev(interface);
-
-       if (!usb_match_device(dev, id))
-               return 0;
-
        /* The interface class, subclass, and protocol should never be
         * checked for a match if the device class is Vendor Specific,
         * unless the match record specifies the Vendor ID. */
@@ -580,6 +573,26 @@ int usb_match_one_id(struct usb_interface *interface,
 
        return 1;
 }
+
+/* returns 0 if no match, 1 if match */
+int usb_match_one_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 0;
+
+       intf = interface->cur_altsetting;
+       dev = interface_to_usbdev(interface);
+
+       if (!usb_match_device(dev, id))
+               return 0;
+
+       return usb_match_one_id_intf(dev, intf, id);
+}
 EXPORT_SYMBOL_GPL(usb_match_one_id);
 
 /**
@@ -1197,8 +1210,13 @@ static int usb_suspend_both(struct usb_device *udev, pm_message_t msg)
        if (status == 0) {
                status = usb_suspend_device(udev, msg);
 
-               /* Again, ignore errors during system sleep transitions */
-               if (!PMSG_IS_AUTO(msg))
+               /*
+                * Ignore errors from non-root-hub devices during
+                * system sleep transitions.  For the most part,
+                * these devices should go to low power anyway when
+                * the entire bus is suspended.
+                */
+               if (udev->parent && !PMSG_IS_AUTO(msg))
                        status = 0;
        }
 
@@ -1650,6 +1668,18 @@ static int autosuspend_check(struct usb_device *udev)
                dev_dbg(&udev->dev, "remote wakeup needed for autosuspend\n");
                return -EOPNOTSUPP;
        }
+
+       /*
+        * If the device is a direct child of the root hub and the HCD
+        * doesn't handle wakeup requests, don't allow autosuspend when
+        * wakeup is needed.
+        */
+       if (w && udev->parent == udev->bus->root_hub &&
+                       bus_to_hcd(udev->bus)->cant_recv_wakeups) {
+               dev_dbg(&udev->dev, "HCD doesn't handle wakeup requests\n");
+               return -EOPNOTSUPP;
+       }
+
        udev->do_remote_wakeup = w;
        return 0;
 }
@@ -1672,10 +1702,13 @@ int usb_runtime_suspend(struct device *dev)
        if (status == -EAGAIN || status == -EBUSY)
                usb_mark_last_busy(udev);
 
-       /* The PM core reacts badly unless the return code is 0,
-        * -EAGAIN, or -EBUSY, so always return -EBUSY on an error.
+       /*
+        * The PM core reacts badly unless the return code is 0,
+        * -EAGAIN, or -EBUSY, so always return -EBUSY on an error
+        * (except for root hubs, because they don't suspend through
+        * an upstream port like other USB devices).
         */
-       if (status != 0)
+       if (status != 0 && udev->parent)
                return -EBUSY;
        return status;
 }