diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/usb/core/hub.c | 114 |
1 files changed, 80 insertions, 34 deletions
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 087e3bb70e09..df68e2562582 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c | |||
@@ -644,6 +644,81 @@ static void hub_stop(struct usb_hub *hub) | |||
644 | hub_quiesce(hub); | 644 | hub_quiesce(hub); |
645 | } | 645 | } |
646 | 646 | ||
647 | #define HUB_RESET 1 | ||
648 | #define HUB_RESUME 2 | ||
649 | #define HUB_RESET_RESUME 3 | ||
650 | |||
651 | #ifdef CONFIG_PM | ||
652 | |||
653 | static void hub_restart(struct usb_hub *hub, int type) | ||
654 | { | ||
655 | struct usb_device *hdev = hub->hdev; | ||
656 | int port1; | ||
657 | |||
658 | /* Check each of the children to see if they require | ||
659 | * USB-PERSIST handling or disconnection. Also check | ||
660 | * each unoccupied port to make sure it is still disabled. | ||
661 | */ | ||
662 | for (port1 = 1; port1 <= hdev->maxchild; ++port1) { | ||
663 | struct usb_device *udev = hdev->children[port1-1]; | ||
664 | int status = 0; | ||
665 | u16 portstatus, portchange; | ||
666 | |||
667 | if (!udev || udev->state == USB_STATE_NOTATTACHED) { | ||
668 | if (type != HUB_RESET) { | ||
669 | status = hub_port_status(hub, port1, | ||
670 | &portstatus, &portchange); | ||
671 | if (status == 0 && (portstatus & | ||
672 | USB_PORT_STAT_ENABLE)) | ||
673 | clear_port_feature(hdev, port1, | ||
674 | USB_PORT_FEAT_ENABLE); | ||
675 | } | ||
676 | continue; | ||
677 | } | ||
678 | |||
679 | /* Was the power session lost while we were suspended? */ | ||
680 | switch (type) { | ||
681 | case HUB_RESET_RESUME: | ||
682 | portstatus = 0; | ||
683 | portchange = USB_PORT_STAT_C_CONNECTION; | ||
684 | break; | ||
685 | |||
686 | case HUB_RESET: | ||
687 | case HUB_RESUME: | ||
688 | status = hub_port_status(hub, port1, | ||
689 | &portstatus, &portchange); | ||
690 | break; | ||
691 | } | ||
692 | |||
693 | /* For "USB_PERSIST"-enabled children we must | ||
694 | * mark the child device for reset-resume and | ||
695 | * turn off the various status changes to prevent | ||
696 | * khubd from disconnecting it later. | ||
697 | */ | ||
698 | if (USB_PERSIST && udev->persist_enabled && status == 0 && | ||
699 | !(portstatus & USB_PORT_STAT_ENABLE)) { | ||
700 | if (portchange & USB_PORT_STAT_C_ENABLE) | ||
701 | clear_port_feature(hub->hdev, port1, | ||
702 | USB_PORT_FEAT_C_ENABLE); | ||
703 | if (portchange & USB_PORT_STAT_C_CONNECTION) | ||
704 | clear_port_feature(hub->hdev, port1, | ||
705 | USB_PORT_FEAT_C_CONNECTION); | ||
706 | udev->reset_resume = 1; | ||
707 | } | ||
708 | |||
709 | /* Otherwise for a reset_resume we must disconnect the child, | ||
710 | * but as we may not lock the child device here | ||
711 | * we have to do a "logical" disconnect. | ||
712 | */ | ||
713 | else if (type == HUB_RESET_RESUME) | ||
714 | hub_port_logical_disconnect(hub, port1); | ||
715 | } | ||
716 | |||
717 | hub_activate(hub); | ||
718 | } | ||
719 | |||
720 | #endif /* CONFIG_PM */ | ||
721 | |||
647 | /* caller has locked the hub device */ | 722 | /* caller has locked the hub device */ |
648 | static int hub_pre_reset(struct usb_interface *intf) | 723 | static int hub_pre_reset(struct usb_interface *intf) |
649 | { | 724 | { |
@@ -2015,49 +2090,20 @@ static int hub_suspend(struct usb_interface *intf, pm_message_t msg) | |||
2015 | 2090 | ||
2016 | static int hub_resume(struct usb_interface *intf) | 2091 | static int hub_resume(struct usb_interface *intf) |
2017 | { | 2092 | { |
2018 | struct usb_hub *hub = usb_get_intfdata (intf); | 2093 | struct usb_hub *hub = usb_get_intfdata(intf); |
2019 | |||
2020 | dev_dbg(&intf->dev, "%s\n", __FUNCTION__); | ||
2021 | 2094 | ||
2022 | /* tell khubd to look for changes on this hub */ | 2095 | dev_dbg(&intf->dev, "%s\n", __func__); |
2023 | hub_activate(hub); | 2096 | hub_restart(hub, HUB_RESUME); |
2024 | return 0; | 2097 | return 0; |
2025 | } | 2098 | } |
2026 | 2099 | ||
2027 | static int hub_reset_resume(struct usb_interface *intf) | 2100 | static int hub_reset_resume(struct usb_interface *intf) |
2028 | { | 2101 | { |
2029 | struct usb_hub *hub = usb_get_intfdata(intf); | 2102 | struct usb_hub *hub = usb_get_intfdata(intf); |
2030 | struct usb_device *hdev = hub->hdev; | ||
2031 | int port1; | ||
2032 | 2103 | ||
2104 | dev_dbg(&intf->dev, "%s\n", __func__); | ||
2033 | hub_power_on(hub); | 2105 | hub_power_on(hub); |
2034 | 2106 | hub_restart(hub, HUB_RESET_RESUME); | |
2035 | for (port1 = 1; port1 <= hdev->maxchild; ++port1) { | ||
2036 | struct usb_device *child = hdev->children[port1-1]; | ||
2037 | |||
2038 | if (child) { | ||
2039 | |||
2040 | /* For "USB_PERSIST"-enabled children we must | ||
2041 | * mark the child device for reset-resume and | ||
2042 | * turn off the connect-change status to prevent | ||
2043 | * khubd from disconnecting it later. | ||
2044 | */ | ||
2045 | if (USB_PERSIST && child->persist_enabled) { | ||
2046 | child->reset_resume = 1; | ||
2047 | clear_port_feature(hdev, port1, | ||
2048 | USB_PORT_FEAT_C_CONNECTION); | ||
2049 | |||
2050 | /* Otherwise we must disconnect the child, | ||
2051 | * but as we may not lock the child device here | ||
2052 | * we have to do a "logical" disconnect. | ||
2053 | */ | ||
2054 | } else { | ||
2055 | hub_port_logical_disconnect(hub, port1); | ||
2056 | } | ||
2057 | } | ||
2058 | } | ||
2059 | |||
2060 | hub_activate(hub); | ||
2061 | return 0; | 2107 | return 0; |
2062 | } | 2108 | } |
2063 | 2109 | ||