aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb
diff options
context:
space:
mode:
authorAlan Stern <stern@rowland.harvard.edu>2008-03-03 15:15:51 -0500
committerGreg Kroah-Hartman <gregkh@suse.de>2008-04-25 00:16:32 -0400
commit5e6effaed6da94e727cd45f945ad2489af8570b3 (patch)
tree2995db2a93c931f3e4a7e4e3203870d5d7f93eb6 /drivers/usb
parent3eb14915a300f539f271e3716f2421bb0697ed48 (diff)
USB: make USB-PERSIST work after every system sleep
This patch (as1046) makes USB-PERSIST work more in accordance with the documentation. Currently it takes effect only in cases where the root hub has lost power or been reset, but it is supposed to operate whenever a power session was dropped during a system sleep. A new hub_restart() routine carries out the duties required during a reset or a reset-resume. It checks to see whether occupied ports are still enabled, and if they aren't then it clears the enable-change and connect-change features (to prevent interference by khubd) and sets the child device's reset_resume flag. It also checks ports that are supposed to be unoccupied to verify that the firmware hasn't left the port in an enabled state. Signed-off-by: Alan Stern <stern@rowland.harvard.edu> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb')
-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