diff options
| -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 | ||
