]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/usb/core/generic.c
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input
[linux-2.6-omap-h63xx.git] / drivers / usb / core / generic.c
index ebb20ff7ac583b41ace5b1ed8c2201fe35667f28..b2fc2b115256633a2f19bfd2a013ffaffedcaf28 100644 (file)
 
 #include <linux/usb.h>
 #include "usb.h"
+#include "hcd.h"
 
 static inline const char *plural(int n)
 {
        return (n == 1 ? "" : "s");
 }
 
+static int is_rndis(struct usb_interface_descriptor *desc)
+{
+       return desc->bInterfaceClass == USB_CLASS_COMM
+               && desc->bInterfaceSubClass == 2
+               && desc->bInterfaceProtocol == 0xff;
+}
+
+static int is_activesync(struct usb_interface_descriptor *desc)
+{
+       return desc->bInterfaceClass == USB_CLASS_MISC
+               && desc->bInterfaceSubClass == 1
+               && desc->bInterfaceProtocol == 1;
+}
+
 static int choose_configuration(struct usb_device *udev)
 {
        int i;
@@ -87,14 +102,12 @@ static int choose_configuration(struct usb_device *udev)
                        continue;
                }
 
-               /* If the first config's first interface is COMM/2/0xff
-                * (MSFT RNDIS), rule it out unless Linux has host-side
-                * RNDIS support. */
-               if (i == 0 && desc
-                               && desc->bInterfaceClass == USB_CLASS_COMM
-                               && desc->bInterfaceSubClass == 2
-                               && desc->bInterfaceProtocol == 0xff) {
-#ifndef CONFIG_USB_NET_RNDIS_HOST
+               /* When the first config's first interface is one of Microsoft's
+                * pet nonstandard Ethernet-over-USB protocols, ignore it unless
+                * this kernel has enabled the necessary host side driver.
+                */
+               if (i == 0 && desc && (is_rndis(desc) || is_activesync(desc))) {
+#if !defined(CONFIG_USB_NET_RNDIS_HOST) && !defined(CONFIG_USB_NET_RNDIS_HOST_MODULE)
                        continue;
 #else
                        best = c;
@@ -172,7 +185,7 @@ static void generic_disconnect(struct usb_device *udev)
        /* if this is only an unbind, not a physical disconnect, then
         * unconfigure the device */
        if (udev->actconfig)
-               usb_set_configuration(udev, 0);
+               usb_set_configuration(udev, -1);
 
        usb_remove_sysfs_dev_files(udev);
 }
@@ -181,16 +194,34 @@ static void generic_disconnect(struct usb_device *udev)
 
 static int generic_suspend(struct usb_device *udev, pm_message_t msg)
 {
-       /* 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).
+       int rc;
+
+       /* Normal USB devices suspend through their upstream port.
+        * Root hubs don't have upstream ports to suspend,
+        * so we have to shut down their downstream HC-to-USB
+        * interfaces manually by doing a bus (or "global") suspend.
         */
-       return usb_port_suspend(udev);
+       if (!udev->parent)
+               rc = hcd_bus_suspend(udev);
+       else
+               rc = usb_port_suspend(udev);
+       return rc;
 }
 
 static int generic_resume(struct usb_device *udev)
 {
-       return usb_port_resume(udev);
+       int rc;
+
+       /* Normal USB devices resume/reset through their upstream port.
+        * Root hubs don't have upstream ports to resume or reset,
+        * so we have to start up their downstream HC-to-USB
+        * interfaces manually by doing a bus (or "global") resume.
+        */
+       if (!udev->parent)
+               rc = hcd_bus_resume(udev);
+       else
+               rc = usb_port_resume(udev);
+       return rc;
 }
 
 #endif /* CONFIG_PM */