diff options
author | Alan Stern <stern@rowland.harvard.edu> | 2006-08-11 16:52:39 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2006-09-27 14:58:54 -0400 |
commit | d5cbad4b8b37acfde3e63d31b92561b87288ad0f (patch) | |
tree | 98869f0db85e1f257c5ddd13a2afda934626153a /drivers | |
parent | 455b25fb209c8241e2163b491228b28667d82c1c (diff) |
usbcore: khubd and busy-port handling
We don't want khubd to start interfering in the device-resume process
merely because the PORT_STATUS_C_SUSPEND feature happens to be set.
Ports need to be marked as busy while a resume is taking place.
In addition, so long as ports are marked as busy, khubd won't be able to
clear their various status-change features. On an interrupt-driven root
hub this could lead to an interrupt storm. Root hub IRQs should not be
re-enabled until the busy_bits value is equal to 0.
This patch (as765) fixes these two potential problems.
Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/usb/core/hub.c | 11 | ||||
-rw-r--r-- | drivers/usb/core/hub.h | 3 |
2 files changed, 12 insertions, 2 deletions
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 3924dd080bea..bdf5be099650 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c | |||
@@ -1658,6 +1658,8 @@ hub_port_resume(struct usb_hub *hub, int port1, struct usb_device *udev) | |||
1658 | 1658 | ||
1659 | // dev_dbg(hub->intfdev, "resume port %d\n", port1); | 1659 | // dev_dbg(hub->intfdev, "resume port %d\n", port1); |
1660 | 1660 | ||
1661 | set_bit(port1, hub->busy_bits); | ||
1662 | |||
1661 | /* see 7.1.7.7; affects power usage, but not budgeting */ | 1663 | /* see 7.1.7.7; affects power usage, but not budgeting */ |
1662 | status = clear_port_feature(hub->hdev, | 1664 | status = clear_port_feature(hub->hdev, |
1663 | port1, USB_PORT_FEAT_SUSPEND); | 1665 | port1, USB_PORT_FEAT_SUSPEND); |
@@ -1707,6 +1709,10 @@ hub_port_resume(struct usb_hub *hub, int port1, struct usb_device *udev) | |||
1707 | if (status < 0) | 1709 | if (status < 0) |
1708 | hub_port_logical_disconnect(hub, port1); | 1710 | hub_port_logical_disconnect(hub, port1); |
1709 | 1711 | ||
1712 | clear_bit(port1, hub->busy_bits); | ||
1713 | if (!hub->hdev->parent && !hub->busy_bits[0]) | ||
1714 | usb_enable_root_hub_irq(hub->hdev->bus); | ||
1715 | |||
1710 | return status; | 1716 | return status; |
1711 | } | 1717 | } |
1712 | 1718 | ||
@@ -2690,7 +2696,7 @@ static void hub_events(void) | |||
2690 | 2696 | ||
2691 | /* If this is a root hub, tell the HCD it's okay to | 2697 | /* If this is a root hub, tell the HCD it's okay to |
2692 | * re-enable port-change interrupts now. */ | 2698 | * re-enable port-change interrupts now. */ |
2693 | if (!hdev->parent) | 2699 | if (!hdev->parent && !hub->busy_bits[0]) |
2694 | usb_enable_root_hub_irq(hdev->bus); | 2700 | usb_enable_root_hub_irq(hdev->bus); |
2695 | 2701 | ||
2696 | loop: | 2702 | loop: |
@@ -2865,6 +2871,9 @@ int usb_reset_device(struct usb_device *udev) | |||
2865 | break; | 2871 | break; |
2866 | } | 2872 | } |
2867 | clear_bit(port1, parent_hub->busy_bits); | 2873 | clear_bit(port1, parent_hub->busy_bits); |
2874 | if (!parent_hdev->parent && !parent_hub->busy_bits[0]) | ||
2875 | usb_enable_root_hub_irq(parent_hdev->bus); | ||
2876 | |||
2868 | if (ret < 0) | 2877 | if (ret < 0) |
2869 | goto re_enumerate; | 2878 | goto re_enumerate; |
2870 | 2879 | ||
diff --git a/drivers/usb/core/hub.h b/drivers/usb/core/hub.h index 29d5f45a8456..0f8e82a4d480 100644 --- a/drivers/usb/core/hub.h +++ b/drivers/usb/core/hub.h | |||
@@ -212,7 +212,8 @@ struct usb_hub { | |||
212 | unsigned long event_bits[1]; /* status change bitmask */ | 212 | unsigned long event_bits[1]; /* status change bitmask */ |
213 | unsigned long change_bits[1]; /* ports with logical connect | 213 | unsigned long change_bits[1]; /* ports with logical connect |
214 | status change */ | 214 | status change */ |
215 | unsigned long busy_bits[1]; /* ports being reset */ | 215 | unsigned long busy_bits[1]; /* ports being reset or |
216 | resumed */ | ||
216 | #if USB_MAXCHILDREN > 31 /* 8*sizeof(unsigned long) - 1 */ | 217 | #if USB_MAXCHILDREN > 31 /* 8*sizeof(unsigned long) - 1 */ |
217 | #error event_bits[] is too short! | 218 | #error event_bits[] is too short! |
218 | #endif | 219 | #endif |