aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/core
diff options
context:
space:
mode:
authorAlan Stern <stern@rowland.harvard.edu>2008-04-28 11:06:42 -0400
committerGreg Kroah-Hartman <gregkh@suse.de>2008-07-21 18:15:50 -0400
commit6ee0b270c733027b2b716b1c80b9aced41e08d20 (patch)
tree8ad1db0a6bb5410a75381e98cf1ecfe390d6322d /drivers/usb/core
parent9e5eace734a7b4e96a4ba4cf1f85622446e95e17 (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.c7
-rw-r--r--drivers/usb/core/hub.c114
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 638enum 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
647static void hub_restart(struct usb_hub *hub, int type) 642static 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 */
717static int hub_pre_reset(struct usb_interface *intf) 716static 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])