diff options
author | Alan Stern <stern@rowland.harvard.edu> | 2008-04-28 11:06:28 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2008-07-21 18:15:47 -0400 |
commit | 24618b0cd42f936cda461bdf6144670a5c925178 (patch) | |
tree | dfcc0d831eb24f7466bbb3ebb2a69b3d93622120 /drivers/usb/core/hub.c | |
parent | b01b03f3ad82b4293f6ca4da9b2692b6a377c609 (diff) |
USB: debounce before unregistering
This patch (as1080) makes a significant change to the way khubd
handles port connect-change and enable-change events. Both types of
event are now debounced, and the debouncing is carried out _before_ an
existing usb_device is unregistered, instead of afterward.
This means that drivers will have to deal with longer runs of errors
when a device is unplugged, but they are supposed to be prepared for
that in any case.
The advantage is that when an enable-change occurs (caused for example
by electromagnetic interference), the debouncing period will provide
time for the cause of the problem to die away. A simple port reset
(added in a forthcoming patch) will then allow us to recover from the
fault.
Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb/core/hub.c')
-rw-r--r-- | drivers/usb/core/hub.c | 31 |
1 files changed, 19 insertions, 12 deletions
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index d14da2123eb5..d741b9457427 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c | |||
@@ -2673,9 +2673,10 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1, | |||
2673 | struct usb_device *hdev = hub->hdev; | 2673 | struct usb_device *hdev = hub->hdev; |
2674 | struct device *hub_dev = hub->intfdev; | 2674 | struct device *hub_dev = hub->intfdev; |
2675 | struct usb_hcd *hcd = bus_to_hcd(hdev->bus); | 2675 | struct usb_hcd *hcd = bus_to_hcd(hdev->bus); |
2676 | u16 wHubCharacteristics = le16_to_cpu(hub->descriptor->wHubCharacteristics); | 2676 | unsigned wHubCharacteristics = |
2677 | le16_to_cpu(hub->descriptor->wHubCharacteristics); | ||
2677 | int status, i; | 2678 | int status, i; |
2678 | 2679 | ||
2679 | dev_dbg (hub_dev, | 2680 | dev_dbg (hub_dev, |
2680 | "port %d, status %04x, change %04x, %s\n", | 2681 | "port %d, status %04x, change %04x, %s\n", |
2681 | port1, portstatus, portchange, portspeed (portstatus)); | 2682 | port1, portstatus, portchange, portspeed (portstatus)); |
@@ -2684,30 +2685,36 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1, | |||
2684 | set_port_led(hub, port1, HUB_LED_AUTO); | 2685 | set_port_led(hub, port1, HUB_LED_AUTO); |
2685 | hub->indicator[port1-1] = INDICATOR_AUTO; | 2686 | hub->indicator[port1-1] = INDICATOR_AUTO; |
2686 | } | 2687 | } |
2687 | |||
2688 | /* Disconnect any existing devices under this port */ | ||
2689 | if (hdev->children[port1-1]) | ||
2690 | usb_disconnect(&hdev->children[port1-1]); | ||
2691 | clear_bit(port1, hub->change_bits); | ||
2692 | 2688 | ||
2693 | #ifdef CONFIG_USB_OTG | 2689 | #ifdef CONFIG_USB_OTG |
2694 | /* during HNP, don't repeat the debounce */ | 2690 | /* during HNP, don't repeat the debounce */ |
2695 | if (hdev->bus->is_b_host) | 2691 | if (hdev->bus->is_b_host) |
2696 | portchange &= ~USB_PORT_STAT_C_CONNECTION; | 2692 | portchange &= ~(USB_PORT_STAT_C_CONNECTION | |
2693 | USB_PORT_STAT_C_ENABLE); | ||
2697 | #endif | 2694 | #endif |
2698 | 2695 | ||
2699 | if (portchange & USB_PORT_STAT_C_CONNECTION) { | 2696 | /* Try to use the debounce delay for protection against |
2697 | * port-enable changes caused, for example, by EMI. | ||
2698 | */ | ||
2699 | if (portchange & (USB_PORT_STAT_C_CONNECTION | | ||
2700 | USB_PORT_STAT_C_ENABLE)) { | ||
2700 | status = hub_port_debounce(hub, port1); | 2701 | status = hub_port_debounce(hub, port1); |
2701 | if (status < 0) { | 2702 | if (status < 0) { |
2702 | if (printk_ratelimit()) | 2703 | if (printk_ratelimit()) |
2703 | dev_err (hub_dev, "connect-debounce failed, " | 2704 | dev_err (hub_dev, "connect-debounce failed, " |
2704 | "port %d disabled\n", port1); | 2705 | "port %d disabled\n", port1); |
2705 | goto done; | 2706 | portstatus &= ~USB_PORT_STAT_CONNECTION; |
2707 | } else { | ||
2708 | portstatus = status; | ||
2706 | } | 2709 | } |
2707 | portstatus = status; | ||
2708 | } | 2710 | } |
2709 | 2711 | ||
2710 | /* Return now if nothing is connected */ | 2712 | /* Disconnect any existing devices under this port */ |
2713 | if (hdev->children[port1-1]) | ||
2714 | usb_disconnect(&hdev->children[port1-1]); | ||
2715 | clear_bit(port1, hub->change_bits); | ||
2716 | |||
2717 | /* Return now if debouncing failed or nothing is connected */ | ||
2711 | if (!(portstatus & USB_PORT_STAT_CONNECTION)) { | 2718 | if (!(portstatus & USB_PORT_STAT_CONNECTION)) { |
2712 | 2719 | ||
2713 | /* maybe switch power back on (e.g. root hub was reset) */ | 2720 | /* maybe switch power back on (e.g. root hub was reset) */ |