aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/usb/core/hub.c114
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
653static 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 */
648static int hub_pre_reset(struct usb_interface *intf) 723static 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
2016static int hub_resume(struct usb_interface *intf) 2091static 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
2027static int hub_reset_resume(struct usb_interface *intf) 2100static 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