]> git.karo-electronics.de Git - linux-beck.git/blobdiff - drivers/usb/core/hub.c
Merge master.kernel.org:/pub/scm/linux/kernel/git/gregkh/usb-2.6
[linux-beck.git] / drivers / usb / core / hub.c
index 650d5ee5871b2f1e6c3f8d7f628c11cd91bed2b1..8e65f7a237e4f68d09a87074bef6e2ed98679dfb 100644 (file)
@@ -22,6 +22,7 @@
 #include <linux/usb.h>
 #include <linux/usbdevice_fs.h>
 #include <linux/kthread.h>
+#include <linux/mutex.h>
 
 #include <asm/semaphore.h>
 #include <asm/uaccess.h>
@@ -1005,12 +1006,18 @@ void usb_set_device_state(struct usb_device *udev,
                ;       /* do nothing */
        else if (new_state != USB_STATE_NOTATTACHED) {
                udev->state = new_state;
-               if (new_state == USB_STATE_CONFIGURED)
-                       device_init_wakeup(&udev->dev,
-                               (udev->actconfig->desc.bmAttributes
-                                & USB_CONFIG_ATT_WAKEUP));
-               else if (new_state != USB_STATE_SUSPENDED)
-                       device_init_wakeup(&udev->dev, 0);
+
+               /* root hub wakeup capabilities are managed out-of-band
+                * and may involve silicon errata ... ignore them here.
+                */
+               if (udev->parent) {
+                       if (new_state == USB_STATE_CONFIGURED)
+                               device_init_wakeup(&udev->dev,
+                                       (udev->actconfig->desc.bmAttributes
+                                        & USB_CONFIG_ATT_WAKEUP));
+                       else if (new_state != USB_STATE_SUSPENDED)
+                               device_init_wakeup(&udev->dev, 0);
+               }
        } else
                recursively_mark_NOTATTACHED(udev);
        spin_unlock_irqrestore(&device_state_lock, flags);
@@ -1172,8 +1179,11 @@ static int choose_configuration(struct usb_device *udev)
        c = udev->config;
        num_configs = udev->descriptor.bNumConfigurations;
        for (i = 0; i < num_configs; (i++, c++)) {
-               struct usb_interface_descriptor *desc =
-                               &c->intf_cache[0]->altsetting->desc;
+               struct usb_interface_descriptor *desc = NULL;
+
+               /* It's possible that a config has no interfaces! */
+               if (c->desc.bNumInterfaces > 0)
+                       desc = &c->intf_cache[0]->altsetting->desc;
 
                /*
                 * HP's USB bus-powered keyboard has only one configuration
@@ -1208,7 +1218,8 @@ static int choose_configuration(struct usb_device *udev)
                /* 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->bInterfaceClass == USB_CLASS_COMM
+               if (i == 0 && desc
+                               && desc->bInterfaceClass == USB_CLASS_COMM
                                && desc->bInterfaceSubClass == 2
                                && desc->bInterfaceProtocol == 0xff) {
 #ifndef CONFIG_USB_NET_RNDIS
@@ -1224,8 +1235,8 @@ static int choose_configuration(struct usb_device *udev)
                 * than a vendor-specific driver. */
                else if (udev->descriptor.bDeviceClass !=
                                                USB_CLASS_VENDOR_SPEC &&
-                               desc->bInterfaceClass !=
-                                               USB_CLASS_VENDOR_SPEC) {
+                               (!desc || desc->bInterfaceClass !=
+                                               USB_CLASS_VENDOR_SPEC)) {
                        best = c;
                        break;
                }
@@ -1876,18 +1887,18 @@ int usb_resume_device(struct usb_device *udev)
        if (udev->state == USB_STATE_NOTATTACHED)
                return -ENODEV;
 
-#ifdef CONFIG_USB_SUSPEND
        /* selective resume of one downstream hub-to-device port */
        if (udev->parent) {
+#ifdef CONFIG_USB_SUSPEND
                if (udev->state == USB_STATE_SUSPENDED) {
                        // NOTE swsusp may bork us, device state being wrong...
                        // NOTE this fails if parent is also suspended...
                        status = hub_port_resume(hdev_to_hub(udev->parent),
                                        udev->portnum, udev);
                } else
+#endif
                        status = 0;
        } else
-#endif
                status = finish_device_resume(udev);
        if (status < 0)
                dev_dbg(&udev->dev, "can't resume, status %d\n",
@@ -2162,7 +2173,7 @@ static int
 hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
                int retry_counter)
 {
-       static DECLARE_MUTEX(usb_address0_sem);
+       static DEFINE_MUTEX(usb_address0_mutex);
 
        struct usb_device       *hdev = hub->hdev;
        int                     i, j, retval;
@@ -2183,7 +2194,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
        if (oldspeed == USB_SPEED_LOW)
                delay = HUB_LONG_RESET_TIME;
 
-       down(&usb_address0_sem);
+       mutex_lock(&usb_address0_mutex);
 
        /* Reset the device; full speed may morph to high speed */
        retval = hub_port_reset(hub, port1, udev, delay);
@@ -2381,7 +2392,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
 fail:
        if (retval)
                hub_port_disable(hub, port1, 0);
-       up(&usb_address0_sem);
+       mutex_unlock(&usb_address0_mutex);
        return retval;
 }
 
@@ -3017,7 +3028,7 @@ int usb_reset_device(struct usb_device *udev)
        parent_hub = hdev_to_hub(parent_hdev);
 
        /* If we're resetting an active hub, take some special actions */
-       if (udev->actconfig &&
+       if (udev->actconfig && udev->actconfig->desc.bNumInterfaces > 0 &&
                        udev->actconfig->interface[0]->dev.driver ==
                                &hub_driver.driver &&
                        (hub = hdev_to_hub(udev)) != NULL) {