diff options
Diffstat (limited to 'drivers/usb/core/hub.c')
-rw-r--r-- | drivers/usb/core/hub.c | 45 |
1 files changed, 28 insertions, 17 deletions
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 650d5ee5871b..8e65f7a237e4 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c | |||
@@ -22,6 +22,7 @@ | |||
22 | #include <linux/usb.h> | 22 | #include <linux/usb.h> |
23 | #include <linux/usbdevice_fs.h> | 23 | #include <linux/usbdevice_fs.h> |
24 | #include <linux/kthread.h> | 24 | #include <linux/kthread.h> |
25 | #include <linux/mutex.h> | ||
25 | 26 | ||
26 | #include <asm/semaphore.h> | 27 | #include <asm/semaphore.h> |
27 | #include <asm/uaccess.h> | 28 | #include <asm/uaccess.h> |
@@ -1005,12 +1006,18 @@ void usb_set_device_state(struct usb_device *udev, | |||
1005 | ; /* do nothing */ | 1006 | ; /* do nothing */ |
1006 | else if (new_state != USB_STATE_NOTATTACHED) { | 1007 | else if (new_state != USB_STATE_NOTATTACHED) { |
1007 | udev->state = new_state; | 1008 | udev->state = new_state; |
1008 | if (new_state == USB_STATE_CONFIGURED) | 1009 | |
1009 | device_init_wakeup(&udev->dev, | 1010 | /* root hub wakeup capabilities are managed out-of-band |
1010 | (udev->actconfig->desc.bmAttributes | 1011 | * and may involve silicon errata ... ignore them here. |
1011 | & USB_CONFIG_ATT_WAKEUP)); | 1012 | */ |
1012 | else if (new_state != USB_STATE_SUSPENDED) | 1013 | if (udev->parent) { |
1013 | device_init_wakeup(&udev->dev, 0); | 1014 | if (new_state == USB_STATE_CONFIGURED) |
1015 | device_init_wakeup(&udev->dev, | ||
1016 | (udev->actconfig->desc.bmAttributes | ||
1017 | & USB_CONFIG_ATT_WAKEUP)); | ||
1018 | else if (new_state != USB_STATE_SUSPENDED) | ||
1019 | device_init_wakeup(&udev->dev, 0); | ||
1020 | } | ||
1014 | } else | 1021 | } else |
1015 | recursively_mark_NOTATTACHED(udev); | 1022 | recursively_mark_NOTATTACHED(udev); |
1016 | spin_unlock_irqrestore(&device_state_lock, flags); | 1023 | spin_unlock_irqrestore(&device_state_lock, flags); |
@@ -1172,8 +1179,11 @@ static int choose_configuration(struct usb_device *udev) | |||
1172 | c = udev->config; | 1179 | c = udev->config; |
1173 | num_configs = udev->descriptor.bNumConfigurations; | 1180 | num_configs = udev->descriptor.bNumConfigurations; |
1174 | for (i = 0; i < num_configs; (i++, c++)) { | 1181 | for (i = 0; i < num_configs; (i++, c++)) { |
1175 | struct usb_interface_descriptor *desc = | 1182 | struct usb_interface_descriptor *desc = NULL; |
1176 | &c->intf_cache[0]->altsetting->desc; | 1183 | |
1184 | /* It's possible that a config has no interfaces! */ | ||
1185 | if (c->desc.bNumInterfaces > 0) | ||
1186 | desc = &c->intf_cache[0]->altsetting->desc; | ||
1177 | 1187 | ||
1178 | /* | 1188 | /* |
1179 | * HP's USB bus-powered keyboard has only one configuration | 1189 | * HP's USB bus-powered keyboard has only one configuration |
@@ -1208,7 +1218,8 @@ static int choose_configuration(struct usb_device *udev) | |||
1208 | /* If the first config's first interface is COMM/2/0xff | 1218 | /* If the first config's first interface is COMM/2/0xff |
1209 | * (MSFT RNDIS), rule it out unless Linux has host-side | 1219 | * (MSFT RNDIS), rule it out unless Linux has host-side |
1210 | * RNDIS support. */ | 1220 | * RNDIS support. */ |
1211 | if (i == 0 && desc->bInterfaceClass == USB_CLASS_COMM | 1221 | if (i == 0 && desc |
1222 | && desc->bInterfaceClass == USB_CLASS_COMM | ||
1212 | && desc->bInterfaceSubClass == 2 | 1223 | && desc->bInterfaceSubClass == 2 |
1213 | && desc->bInterfaceProtocol == 0xff) { | 1224 | && desc->bInterfaceProtocol == 0xff) { |
1214 | #ifndef CONFIG_USB_NET_RNDIS | 1225 | #ifndef CONFIG_USB_NET_RNDIS |
@@ -1224,8 +1235,8 @@ static int choose_configuration(struct usb_device *udev) | |||
1224 | * than a vendor-specific driver. */ | 1235 | * than a vendor-specific driver. */ |
1225 | else if (udev->descriptor.bDeviceClass != | 1236 | else if (udev->descriptor.bDeviceClass != |
1226 | USB_CLASS_VENDOR_SPEC && | 1237 | USB_CLASS_VENDOR_SPEC && |
1227 | desc->bInterfaceClass != | 1238 | (!desc || desc->bInterfaceClass != |
1228 | USB_CLASS_VENDOR_SPEC) { | 1239 | USB_CLASS_VENDOR_SPEC)) { |
1229 | best = c; | 1240 | best = c; |
1230 | break; | 1241 | break; |
1231 | } | 1242 | } |
@@ -1876,18 +1887,18 @@ int usb_resume_device(struct usb_device *udev) | |||
1876 | if (udev->state == USB_STATE_NOTATTACHED) | 1887 | if (udev->state == USB_STATE_NOTATTACHED) |
1877 | return -ENODEV; | 1888 | return -ENODEV; |
1878 | 1889 | ||
1879 | #ifdef CONFIG_USB_SUSPEND | ||
1880 | /* selective resume of one downstream hub-to-device port */ | 1890 | /* selective resume of one downstream hub-to-device port */ |
1881 | if (udev->parent) { | 1891 | if (udev->parent) { |
1892 | #ifdef CONFIG_USB_SUSPEND | ||
1882 | if (udev->state == USB_STATE_SUSPENDED) { | 1893 | if (udev->state == USB_STATE_SUSPENDED) { |
1883 | // NOTE swsusp may bork us, device state being wrong... | 1894 | // NOTE swsusp may bork us, device state being wrong... |
1884 | // NOTE this fails if parent is also suspended... | 1895 | // NOTE this fails if parent is also suspended... |
1885 | status = hub_port_resume(hdev_to_hub(udev->parent), | 1896 | status = hub_port_resume(hdev_to_hub(udev->parent), |
1886 | udev->portnum, udev); | 1897 | udev->portnum, udev); |
1887 | } else | 1898 | } else |
1899 | #endif | ||
1888 | status = 0; | 1900 | status = 0; |
1889 | } else | 1901 | } else |
1890 | #endif | ||
1891 | status = finish_device_resume(udev); | 1902 | status = finish_device_resume(udev); |
1892 | if (status < 0) | 1903 | if (status < 0) |
1893 | dev_dbg(&udev->dev, "can't resume, status %d\n", | 1904 | dev_dbg(&udev->dev, "can't resume, status %d\n", |
@@ -2162,7 +2173,7 @@ static int | |||
2162 | hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1, | 2173 | hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1, |
2163 | int retry_counter) | 2174 | int retry_counter) |
2164 | { | 2175 | { |
2165 | static DECLARE_MUTEX(usb_address0_sem); | 2176 | static DEFINE_MUTEX(usb_address0_mutex); |
2166 | 2177 | ||
2167 | struct usb_device *hdev = hub->hdev; | 2178 | struct usb_device *hdev = hub->hdev; |
2168 | int i, j, retval; | 2179 | int i, j, retval; |
@@ -2183,7 +2194,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1, | |||
2183 | if (oldspeed == USB_SPEED_LOW) | 2194 | if (oldspeed == USB_SPEED_LOW) |
2184 | delay = HUB_LONG_RESET_TIME; | 2195 | delay = HUB_LONG_RESET_TIME; |
2185 | 2196 | ||
2186 | down(&usb_address0_sem); | 2197 | mutex_lock(&usb_address0_mutex); |
2187 | 2198 | ||
2188 | /* Reset the device; full speed may morph to high speed */ | 2199 | /* Reset the device; full speed may morph to high speed */ |
2189 | retval = hub_port_reset(hub, port1, udev, delay); | 2200 | 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, | |||
2381 | fail: | 2392 | fail: |
2382 | if (retval) | 2393 | if (retval) |
2383 | hub_port_disable(hub, port1, 0); | 2394 | hub_port_disable(hub, port1, 0); |
2384 | up(&usb_address0_sem); | 2395 | mutex_unlock(&usb_address0_mutex); |
2385 | return retval; | 2396 | return retval; |
2386 | } | 2397 | } |
2387 | 2398 | ||
@@ -3017,7 +3028,7 @@ int usb_reset_device(struct usb_device *udev) | |||
3017 | parent_hub = hdev_to_hub(parent_hdev); | 3028 | parent_hub = hdev_to_hub(parent_hdev); |
3018 | 3029 | ||
3019 | /* If we're resetting an active hub, take some special actions */ | 3030 | /* If we're resetting an active hub, take some special actions */ |
3020 | if (udev->actconfig && | 3031 | if (udev->actconfig && udev->actconfig->desc.bNumInterfaces > 0 && |
3021 | udev->actconfig->interface[0]->dev.driver == | 3032 | udev->actconfig->interface[0]->dev.driver == |
3022 | &hub_driver.driver && | 3033 | &hub_driver.driver && |
3023 | (hub = hdev_to_hub(udev)) != NULL) { | 3034 | (hub = hdev_to_hub(udev)) != NULL) { |