aboutsummaryrefslogtreecommitdiffstats
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.c62
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 */
2078static int remote_wakeup(struct usb_device *udev) 2072static 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 {