summaryrefslogtreecommitdiffstats
path: root/drivers/usb/core/hub.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/core/hub.c')
-rw-r--r--drivers/usb/core/hub.c45
1 files changed, 28 insertions, 17 deletions
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index 650d5ee5871..8e65f7a237e 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -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) {