diff options
author | Alan Stern <stern@rowland.harvard.edu> | 2008-04-28 11:06:42 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2008-07-21 18:15:50 -0400 |
commit | 6ee0b270c733027b2b716b1c80b9aced41e08d20 (patch) | |
tree | 8ad1db0a6bb5410a75381e98cf1ecfe390d6322d /drivers/usb/core | |
parent | 9e5eace734a7b4e96a4ba4cf1f85622446e95e17 (diff) |
USB: simplify hub_restart() logic
This patch (as1081) straightens out the logic of the hub_restart()
routine. Each port of the hub is scanned and the driver makes sure
that ports which are supposed to be disabled really _are_ disabled.
Any ports with a significant change in status are flagged in
hub->change_bits, so that khubd can focus on them without the need to
scan all the ports a second time -- which means the hub->activating
flag is no longer needed.
Also, it is now recognized explicitly that the only reason for
resuming a port which was not suspended is to carry out a reset-resume
operation, which happens only in a non-CONFIG_USB_SUSPEND setting.
Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb/core')
-rw-r--r-- | drivers/usb/core/driver.c | 7 | ||||
-rw-r--r-- | drivers/usb/core/hub.c | 114 |
2 files changed, 54 insertions, 67 deletions
diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index d0b37d776afe..bf1585b203ca 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c | |||
@@ -805,8 +805,6 @@ static int usb_resume_device(struct usb_device *udev) | |||
805 | 805 | ||
806 | if (udev->state == USB_STATE_NOTATTACHED) | 806 | if (udev->state == USB_STATE_NOTATTACHED) |
807 | goto done; | 807 | goto done; |
808 | if (udev->state != USB_STATE_SUSPENDED && !udev->reset_resume) | ||
809 | goto done; | ||
810 | 808 | ||
811 | /* Can't resume it if it doesn't have a driver. */ | 809 | /* Can't resume it if it doesn't have a driver. */ |
812 | if (udev->dev.driver == NULL) { | 810 | if (udev->dev.driver == NULL) { |
@@ -1173,11 +1171,8 @@ static int usb_resume_both(struct usb_device *udev) | |||
1173 | * then we're stuck. */ | 1171 | * then we're stuck. */ |
1174 | status = usb_resume_device(udev); | 1172 | status = usb_resume_device(udev); |
1175 | } | 1173 | } |
1176 | } else { | 1174 | } else if (udev->reset_resume) |
1177 | |||
1178 | /* Needed for reset-resume */ | ||
1179 | status = usb_resume_device(udev); | 1175 | status = usb_resume_device(udev); |
1180 | } | ||
1181 | 1176 | ||
1182 | if (status == 0 && udev->actconfig) { | 1177 | if (status == 0 && udev->actconfig) { |
1183 | for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) { | 1178 | for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) { |
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 976da1c4919b..054a76dc5d5b 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c | |||
@@ -72,7 +72,6 @@ struct usb_hub { | |||
72 | 72 | ||
73 | unsigned limited_power:1; | 73 | unsigned limited_power:1; |
74 | unsigned quiescing:1; | 74 | unsigned quiescing:1; |
75 | unsigned activating:1; | ||
76 | unsigned disconnected:1; | 75 | unsigned disconnected:1; |
77 | 76 | ||
78 | unsigned has_indicators:1; | 77 | unsigned has_indicators:1; |
@@ -539,7 +538,6 @@ static void hub_quiesce(struct usb_hub *hub) | |||
539 | { | 538 | { |
540 | /* (nonblocking) khubd and related activity won't re-trigger */ | 539 | /* (nonblocking) khubd and related activity won't re-trigger */ |
541 | hub->quiescing = 1; | 540 | hub->quiescing = 1; |
542 | hub->activating = 0; | ||
543 | 541 | ||
544 | /* (blocking) stop khubd and related activity */ | 542 | /* (blocking) stop khubd and related activity */ |
545 | usb_kill_urb(hub->urb); | 543 | usb_kill_urb(hub->urb); |
@@ -554,7 +552,6 @@ static void hub_activate(struct usb_hub *hub) | |||
554 | int status; | 552 | int status; |
555 | 553 | ||
556 | hub->quiescing = 0; | 554 | hub->quiescing = 0; |
557 | hub->activating = 1; | ||
558 | 555 | ||
559 | status = usb_submit_urb(hub->urb, GFP_NOIO); | 556 | status = usb_submit_urb(hub->urb, GFP_NOIO); |
560 | if (status < 0) | 557 | if (status < 0) |
@@ -638,81 +635,83 @@ static void hub_stop(struct usb_hub *hub) | |||
638 | hub_quiesce(hub); | 635 | hub_quiesce(hub); |
639 | } | 636 | } |
640 | 637 | ||
641 | #define HUB_RESET 1 | 638 | enum hub_activation_type { |
642 | #define HUB_RESUME 2 | 639 | HUB_INIT, HUB_POST_RESET, HUB_RESUME, HUB_RESET_RESUME |
643 | #define HUB_RESET_RESUME 3 | 640 | }; |
644 | |||
645 | #ifdef CONFIG_PM | ||
646 | 641 | ||
647 | static void hub_restart(struct usb_hub *hub, int type) | 642 | static void hub_restart(struct usb_hub *hub, enum hub_activation_type type) |
648 | { | 643 | { |
649 | struct usb_device *hdev = hub->hdev; | 644 | struct usb_device *hdev = hub->hdev; |
650 | int port1; | 645 | int port1; |
651 | 646 | ||
652 | /* Check each of the children to see if they require | 647 | /* Check each port and set hub->change_bits to let khubd know |
653 | * USB-PERSIST handling or disconnection. Also check | 648 | * which ports need attention. |
654 | * each unoccupied port to make sure it is still disabled. | ||
655 | */ | 649 | */ |
656 | for (port1 = 1; port1 <= hdev->maxchild; ++port1) { | 650 | for (port1 = 1; port1 <= hdev->maxchild; ++port1) { |
657 | struct usb_device *udev = hdev->children[port1-1]; | 651 | struct usb_device *udev = hdev->children[port1-1]; |
658 | int status = 0; | 652 | int status; |
659 | u16 portstatus, portchange; | 653 | u16 portstatus, portchange; |
660 | 654 | ||
661 | if (!udev || udev->state == USB_STATE_NOTATTACHED) { | 655 | portstatus = portchange = 0; |
662 | if (type != HUB_RESET) { | 656 | status = hub_port_status(hub, port1, &portstatus, &portchange); |
663 | status = hub_port_status(hub, port1, | 657 | if (udev || (portstatus & USB_PORT_STAT_CONNECTION)) |
664 | &portstatus, &portchange); | 658 | dev_dbg(hub->intfdev, |
665 | if (status == 0 && (portstatus & | 659 | "port %d: status %04x change %04x\n", |
666 | USB_PORT_STAT_ENABLE)) | 660 | port1, portstatus, portchange); |
667 | clear_port_feature(hdev, port1, | 661 | |
668 | USB_PORT_FEAT_ENABLE); | 662 | /* After anything other than HUB_RESUME (i.e., initialization |
669 | } | 663 | * or any sort of reset), every port should be disabled. |
670 | continue; | 664 | * Unconnected ports should likewise be disabled (paranoia), |
665 | * and so should ports for which we have no usb_device. | ||
666 | */ | ||
667 | if ((portstatus & USB_PORT_STAT_ENABLE) && ( | ||
668 | type != HUB_RESUME || | ||
669 | !(portstatus & USB_PORT_STAT_CONNECTION) || | ||
670 | !udev || | ||
671 | udev->state == USB_STATE_NOTATTACHED)) { | ||
672 | clear_port_feature(hdev, port1, USB_PORT_FEAT_ENABLE); | ||
673 | portstatus &= ~USB_PORT_STAT_ENABLE; | ||
671 | } | 674 | } |
672 | 675 | ||
673 | /* Was the power session lost while we were suspended? */ | 676 | if (!udev || udev->state == USB_STATE_NOTATTACHED) { |
674 | switch (type) { | 677 | /* Tell khubd to disconnect the device or |
675 | case HUB_RESET_RESUME: | 678 | * check for a new connection |
676 | portstatus = 0; | 679 | */ |
677 | portchange = USB_PORT_STAT_C_CONNECTION; | 680 | if (udev || (portstatus & USB_PORT_STAT_CONNECTION)) |
678 | break; | 681 | set_bit(port1, hub->change_bits); |
679 | 682 | ||
680 | case HUB_RESET: | 683 | } else if (portstatus & USB_PORT_STAT_ENABLE) { |
681 | case HUB_RESUME: | 684 | /* The power session apparently survived the resume. |
682 | status = hub_port_status(hub, port1, | 685 | * If there was an overcurrent or suspend change |
683 | &portstatus, &portchange); | 686 | * (i.e., remote wakeup request), have khubd |
684 | break; | 687 | * take care of it. |
685 | } | 688 | */ |
689 | if (portchange) | ||
690 | set_bit(port1, hub->change_bits); | ||
686 | 691 | ||
687 | /* For "USB_PERSIST"-enabled children we must | 692 | } else if (udev->persist_enabled) { |
688 | * mark the child device for reset-resume and | 693 | /* Turn off the status changes to prevent khubd |
689 | * turn off the various status changes to prevent | 694 | * from disconnecting the device. |
690 | * khubd from disconnecting it later. | 695 | */ |
691 | */ | ||
692 | if (udev->persist_enabled && status == 0 && | ||
693 | !(portstatus & USB_PORT_STAT_ENABLE)) { | ||
694 | if (portchange & USB_PORT_STAT_C_ENABLE) | 696 | if (portchange & USB_PORT_STAT_C_ENABLE) |
695 | clear_port_feature(hub->hdev, port1, | 697 | clear_port_feature(hub->hdev, port1, |
696 | USB_PORT_FEAT_C_ENABLE); | 698 | USB_PORT_FEAT_C_ENABLE); |
697 | if (portchange & USB_PORT_STAT_C_CONNECTION) | 699 | if (portchange & USB_PORT_STAT_C_CONNECTION) |
698 | clear_port_feature(hub->hdev, port1, | 700 | clear_port_feature(hub->hdev, port1, |
699 | USB_PORT_FEAT_C_CONNECTION); | 701 | USB_PORT_FEAT_C_CONNECTION); |
702 | #ifdef CONFIG_PM | ||
700 | udev->reset_resume = 1; | 703 | udev->reset_resume = 1; |
704 | #endif | ||
705 | } else { | ||
706 | /* The power session is gone; tell khubd */ | ||
707 | usb_set_device_state(udev, USB_STATE_NOTATTACHED); | ||
708 | set_bit(port1, hub->change_bits); | ||
701 | } | 709 | } |
702 | |||
703 | /* Otherwise for a reset_resume we must disconnect the child, | ||
704 | * but as we may not lock the child device here | ||
705 | * we have to do a "logical" disconnect. | ||
706 | */ | ||
707 | else if (type == HUB_RESET_RESUME) | ||
708 | hub_port_logical_disconnect(hub, port1); | ||
709 | } | 710 | } |
710 | 711 | ||
711 | hub_activate(hub); | 712 | hub_activate(hub); |
712 | } | 713 | } |
713 | 714 | ||
714 | #endif /* CONFIG_PM */ | ||
715 | |||
716 | /* caller has locked the hub device */ | 715 | /* caller has locked the hub device */ |
717 | static int hub_pre_reset(struct usb_interface *intf) | 716 | static int hub_pre_reset(struct usb_interface *intf) |
718 | { | 717 | { |
@@ -728,7 +727,7 @@ static int hub_post_reset(struct usb_interface *intf) | |||
728 | struct usb_hub *hub = usb_get_intfdata(intf); | 727 | struct usb_hub *hub = usb_get_intfdata(intf); |
729 | 728 | ||
730 | hub_power_on(hub); | 729 | hub_power_on(hub); |
731 | hub_activate(hub); | 730 | hub_restart(hub, HUB_POST_RESET); |
732 | return 0; | 731 | return 0; |
733 | } | 732 | } |
734 | 733 | ||
@@ -2903,7 +2902,7 @@ static void hub_events(void) | |||
2903 | continue; | 2902 | continue; |
2904 | connect_change = test_bit(i, hub->change_bits); | 2903 | connect_change = test_bit(i, hub->change_bits); |
2905 | if (!test_and_clear_bit(i, hub->event_bits) && | 2904 | if (!test_and_clear_bit(i, hub->event_bits) && |
2906 | !connect_change && !hub->activating) | 2905 | !connect_change) |
2907 | continue; | 2906 | continue; |
2908 | 2907 | ||
2909 | ret = hub_port_status(hub, i, | 2908 | ret = hub_port_status(hub, i, |
@@ -2911,11 +2910,6 @@ static void hub_events(void) | |||
2911 | if (ret < 0) | 2910 | if (ret < 0) |
2912 | continue; | 2911 | continue; |
2913 | 2912 | ||
2914 | if (hub->activating && !hdev->children[i-1] && | ||
2915 | (portstatus & | ||
2916 | USB_PORT_STAT_CONNECTION)) | ||
2917 | connect_change = 1; | ||
2918 | |||
2919 | if (portchange & USB_PORT_STAT_C_CONNECTION) { | 2913 | if (portchange & USB_PORT_STAT_C_CONNECTION) { |
2920 | clear_port_feature(hdev, i, | 2914 | clear_port_feature(hdev, i, |
2921 | USB_PORT_FEAT_C_CONNECTION); | 2915 | USB_PORT_FEAT_C_CONNECTION); |
@@ -3011,8 +3005,6 @@ static void hub_events(void) | |||
3011 | } | 3005 | } |
3012 | } | 3006 | } |
3013 | 3007 | ||
3014 | hub->activating = 0; | ||
3015 | |||
3016 | /* If this is a root hub, tell the HCD it's okay to | 3008 | /* If this is a root hub, tell the HCD it's okay to |
3017 | * re-enable port-change interrupts now. */ | 3009 | * re-enable port-change interrupts now. */ |
3018 | if (!hdev->parent && !hub->busy_bits[0]) | 3010 | if (!hdev->parent && !hub->busy_bits[0]) |