diff options
Diffstat (limited to 'drivers/usb/core/hub.c')
-rw-r--r-- | drivers/usb/core/hub.c | 62 |
1 files changed, 48 insertions, 14 deletions
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 054a76dc5d5..8ea095e5909 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c | |||
@@ -690,18 +690,11 @@ static void hub_restart(struct usb_hub *hub, enum hub_activation_type type) | |||
690 | set_bit(port1, hub->change_bits); | 690 | set_bit(port1, hub->change_bits); |
691 | 691 | ||
692 | } else if (udev->persist_enabled) { | 692 | } else if (udev->persist_enabled) { |
693 | /* Turn off the status changes to prevent khubd | ||
694 | * from disconnecting the device. | ||
695 | */ | ||
696 | if (portchange & USB_PORT_STAT_C_ENABLE) | ||
697 | clear_port_feature(hub->hdev, port1, | ||
698 | USB_PORT_FEAT_C_ENABLE); | ||
699 | if (portchange & USB_PORT_STAT_C_CONNECTION) | ||
700 | clear_port_feature(hub->hdev, port1, | ||
701 | USB_PORT_FEAT_C_CONNECTION); | ||
702 | #ifdef CONFIG_PM | 693 | #ifdef CONFIG_PM |
703 | udev->reset_resume = 1; | 694 | udev->reset_resume = 1; |
704 | #endif | 695 | #endif |
696 | set_bit(port1, hub->change_bits); | ||
697 | |||
705 | } else { | 698 | } else { |
706 | /* The power session is gone; tell khubd */ | 699 | /* The power session is gone; tell khubd */ |
707 | usb_set_device_state(udev, USB_STATE_NOTATTACHED); | 700 | usb_set_device_state(udev, USB_STATE_NOTATTACHED); |
@@ -2075,17 +2068,16 @@ int usb_port_resume(struct usb_device *udev) | |||
2075 | return status; | 2068 | return status; |
2076 | } | 2069 | } |
2077 | 2070 | ||
2071 | /* caller has locked udev */ | ||
2078 | static int remote_wakeup(struct usb_device *udev) | 2072 | static int remote_wakeup(struct usb_device *udev) |
2079 | { | 2073 | { |
2080 | int status = 0; | 2074 | int status = 0; |
2081 | 2075 | ||
2082 | usb_lock_device(udev); | ||
2083 | if (udev->state == USB_STATE_SUSPENDED) { | 2076 | if (udev->state == USB_STATE_SUSPENDED) { |
2084 | dev_dbg(&udev->dev, "usb %sresume\n", "wakeup-"); | 2077 | dev_dbg(&udev->dev, "usb %sresume\n", "wakeup-"); |
2085 | usb_mark_last_busy(udev); | 2078 | usb_mark_last_busy(udev); |
2086 | status = usb_external_resume_device(udev); | 2079 | status = usb_external_resume_device(udev); |
2087 | } | 2080 | } |
2088 | usb_unlock_device(udev); | ||
2089 | return status; | 2081 | return status; |
2090 | } | 2082 | } |
2091 | 2083 | ||
@@ -2632,6 +2624,7 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1, | |||
2632 | struct usb_hcd *hcd = bus_to_hcd(hdev->bus); | 2624 | struct usb_hcd *hcd = bus_to_hcd(hdev->bus); |
2633 | unsigned wHubCharacteristics = | 2625 | unsigned wHubCharacteristics = |
2634 | le16_to_cpu(hub->descriptor->wHubCharacteristics); | 2626 | le16_to_cpu(hub->descriptor->wHubCharacteristics); |
2627 | struct usb_device *udev; | ||
2635 | int status, i; | 2628 | int status, i; |
2636 | 2629 | ||
2637 | dev_dbg (hub_dev, | 2630 | dev_dbg (hub_dev, |
@@ -2666,8 +2659,45 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1, | |||
2666 | } | 2659 | } |
2667 | } | 2660 | } |
2668 | 2661 | ||
2662 | /* Try to resuscitate an existing device */ | ||
2663 | udev = hdev->children[port1-1]; | ||
2664 | if ((portstatus & USB_PORT_STAT_CONNECTION) && udev && | ||
2665 | udev->state != USB_STATE_NOTATTACHED) { | ||
2666 | |||
2667 | usb_lock_device(udev); | ||
2668 | if (portstatus & USB_PORT_STAT_ENABLE) { | ||
2669 | status = 0; /* Nothing to do */ | ||
2670 | } else if (!udev->persist_enabled) { | ||
2671 | status = -ENODEV; /* Mustn't resuscitate */ | ||
2672 | |||
2673 | #ifdef CONFIG_USB_SUSPEND | ||
2674 | } else if (udev->state == USB_STATE_SUSPENDED) { | ||
2675 | /* For a suspended device, treat this as a | ||
2676 | * remote wakeup event. | ||
2677 | */ | ||
2678 | if (udev->do_remote_wakeup) | ||
2679 | status = remote_wakeup(udev); | ||
2680 | |||
2681 | /* Otherwise leave it be; devices can't tell the | ||
2682 | * difference between suspended and disabled. | ||
2683 | */ | ||
2684 | else | ||
2685 | status = 0; | ||
2686 | #endif | ||
2687 | |||
2688 | } else { | ||
2689 | status = usb_reset_composite_device(udev, NULL); | ||
2690 | } | ||
2691 | usb_unlock_device(udev); | ||
2692 | |||
2693 | if (status == 0) { | ||
2694 | clear_bit(port1, hub->change_bits); | ||
2695 | return; | ||
2696 | } | ||
2697 | } | ||
2698 | |||
2669 | /* Disconnect any existing devices under this port */ | 2699 | /* Disconnect any existing devices under this port */ |
2670 | if (hdev->children[port1-1]) | 2700 | if (udev) |
2671 | usb_disconnect(&hdev->children[port1-1]); | 2701 | usb_disconnect(&hdev->children[port1-1]); |
2672 | clear_bit(port1, hub->change_bits); | 2702 | clear_bit(port1, hub->change_bits); |
2673 | 2703 | ||
@@ -2685,7 +2715,6 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1, | |||
2685 | } | 2715 | } |
2686 | 2716 | ||
2687 | for (i = 0; i < SET_CONFIG_TRIES; i++) { | 2717 | for (i = 0; i < SET_CONFIG_TRIES; i++) { |
2688 | struct usb_device *udev; | ||
2689 | 2718 | ||
2690 | /* reallocate for each attempt, since references | 2719 | /* reallocate for each attempt, since references |
2691 | * to the previous one can escape in various ways | 2720 | * to the previous one can escape in various ways |
@@ -2944,11 +2973,16 @@ static void hub_events(void) | |||
2944 | } | 2973 | } |
2945 | 2974 | ||
2946 | if (portchange & USB_PORT_STAT_C_SUSPEND) { | 2975 | if (portchange & USB_PORT_STAT_C_SUSPEND) { |
2976 | struct usb_device *udev; | ||
2977 | |||
2947 | clear_port_feature(hdev, i, | 2978 | clear_port_feature(hdev, i, |
2948 | USB_PORT_FEAT_C_SUSPEND); | 2979 | USB_PORT_FEAT_C_SUSPEND); |
2949 | if (hdev->children[i-1]) { | 2980 | udev = hdev->children[i-1]; |
2981 | if (udev) { | ||
2982 | usb_lock_device(udev); | ||
2950 | ret = remote_wakeup(hdev-> | 2983 | ret = remote_wakeup(hdev-> |
2951 | children[i-1]); | 2984 | children[i-1]); |
2985 | usb_unlock_device(udev); | ||
2952 | if (ret < 0) | 2986 | if (ret < 0) |
2953 | connect_change = 1; | 2987 | connect_change = 1; |
2954 | } else { | 2988 | } else { |