diff options
Diffstat (limited to 'drivers/usb/core/hub.c')
-rw-r--r-- | drivers/usb/core/hub.c | 124 |
1 files changed, 57 insertions, 67 deletions
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 3b7151687776..73dfa194160b 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c | |||
@@ -122,12 +122,12 @@ struct usb_hub *usb_hub_to_struct_hub(struct usb_device *hdev) | |||
122 | return usb_get_intfdata(hdev->actconfig->interface[0]); | 122 | return usb_get_intfdata(hdev->actconfig->interface[0]); |
123 | } | 123 | } |
124 | 124 | ||
125 | static int usb_device_supports_lpm(struct usb_device *udev) | 125 | int usb_device_supports_lpm(struct usb_device *udev) |
126 | { | 126 | { |
127 | /* USB 2.1 (and greater) devices indicate LPM support through | 127 | /* USB 2.1 (and greater) devices indicate LPM support through |
128 | * their USB 2.0 Extended Capabilities BOS descriptor. | 128 | * their USB 2.0 Extended Capabilities BOS descriptor. |
129 | */ | 129 | */ |
130 | if (udev->speed == USB_SPEED_HIGH) { | 130 | if (udev->speed == USB_SPEED_HIGH || udev->speed == USB_SPEED_FULL) { |
131 | if (udev->bos->ext_cap && | 131 | if (udev->bos->ext_cap && |
132 | (USB_LPM_SUPPORT & | 132 | (USB_LPM_SUPPORT & |
133 | le32_to_cpu(udev->bos->ext_cap->bmAttributes))) | 133 | le32_to_cpu(udev->bos->ext_cap->bmAttributes))) |
@@ -795,7 +795,8 @@ int usb_hub_clear_tt_buffer(struct urb *urb) | |||
795 | * since each TT has "at least two" buffers that can need it (and | 795 | * since each TT has "at least two" buffers that can need it (and |
796 | * there can be many TTs per hub). even if they're uncommon. | 796 | * there can be many TTs per hub). even if they're uncommon. |
797 | */ | 797 | */ |
798 | if ((clear = kmalloc (sizeof *clear, GFP_ATOMIC)) == NULL) { | 798 | clear = kmalloc(sizeof *clear, GFP_ATOMIC); |
799 | if (clear == NULL) { | ||
799 | dev_err (&udev->dev, "can't save CLEAR_TT_BUFFER state\n"); | 800 | dev_err (&udev->dev, "can't save CLEAR_TT_BUFFER state\n"); |
800 | /* FIXME recover somehow ... RESET_TT? */ | 801 | /* FIXME recover somehow ... RESET_TT? */ |
801 | return -ENOMEM; | 802 | return -ENOMEM; |
@@ -2350,6 +2351,26 @@ static void set_usb_port_removable(struct usb_device *udev) | |||
2350 | 2351 | ||
2351 | hub = usb_hub_to_struct_hub(udev->parent); | 2352 | hub = usb_hub_to_struct_hub(udev->parent); |
2352 | 2353 | ||
2354 | /* | ||
2355 | * If the platform firmware has provided information about a port, | ||
2356 | * use that to determine whether it's removable. | ||
2357 | */ | ||
2358 | switch (hub->ports[udev->portnum - 1]->connect_type) { | ||
2359 | case USB_PORT_CONNECT_TYPE_HOT_PLUG: | ||
2360 | udev->removable = USB_DEVICE_REMOVABLE; | ||
2361 | return; | ||
2362 | case USB_PORT_CONNECT_TYPE_HARD_WIRED: | ||
2363 | case USB_PORT_NOT_USED: | ||
2364 | udev->removable = USB_DEVICE_FIXED; | ||
2365 | return; | ||
2366 | default: | ||
2367 | break; | ||
2368 | } | ||
2369 | |||
2370 | /* | ||
2371 | * Otherwise, check whether the hub knows whether a port is removable | ||
2372 | * or not | ||
2373 | */ | ||
2353 | wHubCharacteristics = le16_to_cpu(hub->descriptor->wHubCharacteristics); | 2374 | wHubCharacteristics = le16_to_cpu(hub->descriptor->wHubCharacteristics); |
2354 | 2375 | ||
2355 | if (!(wHubCharacteristics & HUB_CHAR_COMPOUND)) | 2376 | if (!(wHubCharacteristics & HUB_CHAR_COMPOUND)) |
@@ -2369,21 +2390,6 @@ static void set_usb_port_removable(struct usb_device *udev) | |||
2369 | else | 2390 | else |
2370 | udev->removable = USB_DEVICE_FIXED; | 2391 | udev->removable = USB_DEVICE_FIXED; |
2371 | 2392 | ||
2372 | /* | ||
2373 | * Platform firmware may have populated an alternative value for | ||
2374 | * removable. If the parent port has a known connect_type use | ||
2375 | * that instead. | ||
2376 | */ | ||
2377 | switch (hub->ports[udev->portnum - 1]->connect_type) { | ||
2378 | case USB_PORT_CONNECT_TYPE_HOT_PLUG: | ||
2379 | udev->removable = USB_DEVICE_REMOVABLE; | ||
2380 | break; | ||
2381 | case USB_PORT_CONNECT_TYPE_HARD_WIRED: | ||
2382 | udev->removable = USB_DEVICE_FIXED; | ||
2383 | break; | ||
2384 | default: /* use what was set above */ | ||
2385 | break; | ||
2386 | } | ||
2387 | } | 2393 | } |
2388 | 2394 | ||
2389 | /** | 2395 | /** |
@@ -2616,9 +2622,6 @@ static bool use_new_scheme(struct usb_device *udev, int retry) | |||
2616 | return USE_NEW_SCHEME(retry); | 2622 | return USE_NEW_SCHEME(retry); |
2617 | } | 2623 | } |
2618 | 2624 | ||
2619 | static int hub_port_reset(struct usb_hub *hub, int port1, | ||
2620 | struct usb_device *udev, unsigned int delay, bool warm); | ||
2621 | |||
2622 | /* Is a USB 3.0 port in the Inactive or Compliance Mode state? | 2625 | /* Is a USB 3.0 port in the Inactive or Compliance Mode state? |
2623 | * Port worm reset is required to recover | 2626 | * Port worm reset is required to recover |
2624 | */ | 2627 | */ |
@@ -2706,44 +2709,6 @@ static int hub_port_wait_reset(struct usb_hub *hub, int port1, | |||
2706 | return 0; | 2709 | return 0; |
2707 | } | 2710 | } |
2708 | 2711 | ||
2709 | static void hub_port_finish_reset(struct usb_hub *hub, int port1, | ||
2710 | struct usb_device *udev, int *status) | ||
2711 | { | ||
2712 | switch (*status) { | ||
2713 | case 0: | ||
2714 | /* TRSTRCY = 10 ms; plus some extra */ | ||
2715 | msleep(10 + 40); | ||
2716 | if (udev) { | ||
2717 | struct usb_hcd *hcd = bus_to_hcd(udev->bus); | ||
2718 | |||
2719 | update_devnum(udev, 0); | ||
2720 | /* The xHC may think the device is already reset, | ||
2721 | * so ignore the status. | ||
2722 | */ | ||
2723 | if (hcd->driver->reset_device) | ||
2724 | hcd->driver->reset_device(hcd, udev); | ||
2725 | } | ||
2726 | /* FALL THROUGH */ | ||
2727 | case -ENOTCONN: | ||
2728 | case -ENODEV: | ||
2729 | usb_clear_port_feature(hub->hdev, | ||
2730 | port1, USB_PORT_FEAT_C_RESET); | ||
2731 | if (hub_is_superspeed(hub->hdev)) { | ||
2732 | usb_clear_port_feature(hub->hdev, port1, | ||
2733 | USB_PORT_FEAT_C_BH_PORT_RESET); | ||
2734 | usb_clear_port_feature(hub->hdev, port1, | ||
2735 | USB_PORT_FEAT_C_PORT_LINK_STATE); | ||
2736 | usb_clear_port_feature(hub->hdev, port1, | ||
2737 | USB_PORT_FEAT_C_CONNECTION); | ||
2738 | } | ||
2739 | if (udev) | ||
2740 | usb_set_device_state(udev, *status | ||
2741 | ? USB_STATE_NOTATTACHED | ||
2742 | : USB_STATE_DEFAULT); | ||
2743 | break; | ||
2744 | } | ||
2745 | } | ||
2746 | |||
2747 | /* Handle port reset and port warm(BH) reset (for USB3 protocol ports) */ | 2712 | /* Handle port reset and port warm(BH) reset (for USB3 protocol ports) */ |
2748 | static int hub_port_reset(struct usb_hub *hub, int port1, | 2713 | static int hub_port_reset(struct usb_hub *hub, int port1, |
2749 | struct usb_device *udev, unsigned int delay, bool warm) | 2714 | struct usb_device *udev, unsigned int delay, bool warm) |
@@ -2767,13 +2732,10 @@ static int hub_port_reset(struct usb_hub *hub, int port1, | |||
2767 | * If the caller hasn't explicitly requested a warm reset, | 2732 | * If the caller hasn't explicitly requested a warm reset, |
2768 | * double check and see if one is needed. | 2733 | * double check and see if one is needed. |
2769 | */ | 2734 | */ |
2770 | status = hub_port_status(hub, port1, | 2735 | if (hub_port_status(hub, port1, &portstatus, &portchange) == 0) |
2771 | &portstatus, &portchange); | 2736 | if (hub_port_warm_reset_required(hub, port1, |
2772 | if (status < 0) | 2737 | portstatus)) |
2773 | goto done; | 2738 | warm = true; |
2774 | |||
2775 | if (hub_port_warm_reset_required(hub, port1, portstatus)) | ||
2776 | warm = true; | ||
2777 | } | 2739 | } |
2778 | clear_bit(port1, hub->warm_reset_bits); | 2740 | clear_bit(port1, hub->warm_reset_bits); |
2779 | 2741 | ||
@@ -2799,11 +2761,19 @@ static int hub_port_reset(struct usb_hub *hub, int port1, | |||
2799 | 2761 | ||
2800 | /* Check for disconnect or reset */ | 2762 | /* Check for disconnect or reset */ |
2801 | if (status == 0 || status == -ENOTCONN || status == -ENODEV) { | 2763 | if (status == 0 || status == -ENOTCONN || status == -ENODEV) { |
2802 | hub_port_finish_reset(hub, port1, udev, &status); | 2764 | usb_clear_port_feature(hub->hdev, port1, |
2765 | USB_PORT_FEAT_C_RESET); | ||
2803 | 2766 | ||
2804 | if (!hub_is_superspeed(hub->hdev)) | 2767 | if (!hub_is_superspeed(hub->hdev)) |
2805 | goto done; | 2768 | goto done; |
2806 | 2769 | ||
2770 | usb_clear_port_feature(hub->hdev, port1, | ||
2771 | USB_PORT_FEAT_C_BH_PORT_RESET); | ||
2772 | usb_clear_port_feature(hub->hdev, port1, | ||
2773 | USB_PORT_FEAT_C_PORT_LINK_STATE); | ||
2774 | usb_clear_port_feature(hub->hdev, port1, | ||
2775 | USB_PORT_FEAT_C_CONNECTION); | ||
2776 | |||
2807 | /* | 2777 | /* |
2808 | * If a USB 3.0 device migrates from reset to an error | 2778 | * If a USB 3.0 device migrates from reset to an error |
2809 | * state, re-issue the warm reset. | 2779 | * state, re-issue the warm reset. |
@@ -2836,6 +2806,26 @@ static int hub_port_reset(struct usb_hub *hub, int port1, | |||
2836 | dev_err(&port_dev->dev, "Cannot enable. Maybe the USB cable is bad?\n"); | 2806 | dev_err(&port_dev->dev, "Cannot enable. Maybe the USB cable is bad?\n"); |
2837 | 2807 | ||
2838 | done: | 2808 | done: |
2809 | if (status == 0) { | ||
2810 | /* TRSTRCY = 10 ms; plus some extra */ | ||
2811 | msleep(10 + 40); | ||
2812 | if (udev) { | ||
2813 | struct usb_hcd *hcd = bus_to_hcd(udev->bus); | ||
2814 | |||
2815 | update_devnum(udev, 0); | ||
2816 | /* The xHC may think the device is already reset, | ||
2817 | * so ignore the status. | ||
2818 | */ | ||
2819 | if (hcd->driver->reset_device) | ||
2820 | hcd->driver->reset_device(hcd, udev); | ||
2821 | |||
2822 | usb_set_device_state(udev, USB_STATE_DEFAULT); | ||
2823 | } | ||
2824 | } else { | ||
2825 | if (udev) | ||
2826 | usb_set_device_state(udev, USB_STATE_NOTATTACHED); | ||
2827 | } | ||
2828 | |||
2839 | if (!hub_is_superspeed(hub->hdev)) | 2829 | if (!hub_is_superspeed(hub->hdev)) |
2840 | up_read(&ehci_cf_port_reset_rwsem); | 2830 | up_read(&ehci_cf_port_reset_rwsem); |
2841 | 2831 | ||