Commit 3e399447 authored by Alan Stern's avatar Alan Stern Committed by Greg Kroah-Hartman

[PATCH] USB: Debounce all connect change events

This patch makes the hub driver debounce all connection changes.  Right
now the driver only does so if the status happens to be CONNECTED at one
particular instant.  However, the whole point of debouncing is that the
connection is subject to transient interruptions until it has stabilized;
hence deciding whether to debounce based on a single initial test defeats
the entire purpose.

There are some additional smaller changes that go along with the major
one:

	Comments added to hub_port_connect_change() detailing the
	conditions under which it will be called.

	Don't clear the port's connect-changed feature if it wasn't
	set.

	Skip debouncing if there wasn't a physical connection change
	but only a logical port-enable change (or a firmware-download-
	induced device morph -- not yet implemented).

	Clear all the hub status change indicators in hub_events()
	before handling a connect change.  This will reduce syslog
	clutter from status change bits that remain set while khubd
	is busy taking care of a new device.

The patch includes no changes to the debounce routine itself.  Please
apply.
Signed-off-by: default avatarAlan Stern <stern@rowland.harvard.edu>
Signed-off-by: default avatarGreg Kroah-Hartman <greg@kroah.com>
parent 5609e1a2
...@@ -1487,6 +1487,13 @@ hub_power_remaining (struct usb_hub *hub, struct usb_device *hdev) ...@@ -1487,6 +1487,13 @@ hub_power_remaining (struct usb_hub *hub, struct usb_device *hdev)
return remaining; return remaining;
} }
/* Handle physical or logical connection change events.
* This routine is called when:
* a port connection-change occurs;
* a port enable-change occurs (often caused by EMI);
* usb_reset_device() encounters changed descriptors (as from
* a firmware download)
*/
static void hub_port_connect_change(struct usb_hub *hub, int port, static void hub_port_connect_change(struct usb_hub *hub, int port,
u16 portstatus, u16 portchange) u16 portstatus, u16 portchange)
{ {
...@@ -1498,9 +1505,6 @@ static void hub_port_connect_change(struct usb_hub *hub, int port, ...@@ -1498,9 +1505,6 @@ static void hub_port_connect_change(struct usb_hub *hub, int port,
"port %d, status %04x, change %04x, %s\n", "port %d, status %04x, change %04x, %s\n",
port + 1, portstatus, portchange, portspeed (portstatus)); port + 1, portstatus, portchange, portspeed (portstatus));
/* Clear the connection change status */
clear_port_feature(hdev, port + 1, USB_PORT_FEAT_C_CONNECTION);
if (hub->has_indicators) { if (hub->has_indicators) {
set_port_led(hdev, port + 1, HUB_LED_AUTO); set_port_led(hdev, port + 1, HUB_LED_AUTO);
hub->indicator[port] = INDICATOR_AUTO; hub->indicator[port] = INDICATOR_AUTO;
...@@ -1510,6 +1514,18 @@ static void hub_port_connect_change(struct usb_hub *hub, int port, ...@@ -1510,6 +1514,18 @@ static void hub_port_connect_change(struct usb_hub *hub, int port,
if (hdev->children[port]) if (hdev->children[port])
usb_disconnect(&hdev->children[port]); usb_disconnect(&hdev->children[port]);
if (portchange & USB_PORT_STAT_C_CONNECTION) {
status = hub_port_debounce(hdev, port);
if (status == -ENOTCONN)
portstatus = 0;
else if (status < 0) {
dev_err (hub_dev,
"connect-debounce failed, port %d disabled\n",
port+1);
goto done;
}
}
/* Return now if nothing is connected */ /* Return now if nothing is connected */
if (!(portstatus & USB_PORT_STAT_CONNECTION)) { if (!(portstatus & USB_PORT_STAT_CONNECTION)) {
...@@ -1524,13 +1540,6 @@ static void hub_port_connect_change(struct usb_hub *hub, int port, ...@@ -1524,13 +1540,6 @@ static void hub_port_connect_change(struct usb_hub *hub, int port,
return; return;
} }
if (hub_port_debounce(hdev, port)) {
dev_err (hub_dev,
"connect-debounce failed, port %d disabled\n",
port+1);
goto done;
}
for (i = 0; i < SET_CONFIG_TRIES; i++) { for (i = 0; i < SET_CONFIG_TRIES; i++) {
struct usb_device *udev; struct usb_device *udev;
...@@ -1631,6 +1640,7 @@ static void hub_events(void) ...@@ -1631,6 +1640,7 @@ static void hub_events(void)
u16 portstatus; u16 portstatus;
u16 portchange; u16 portchange;
int i, ret; int i, ret;
int connect_change;
/* /*
* We restart the list every time to avoid a deadlock with * We restart the list every time to avoid a deadlock with
...@@ -1676,15 +1686,21 @@ static void hub_events(void) ...@@ -1676,15 +1686,21 @@ static void hub_events(void)
for (i = 0; i < hub->descriptor->bNbrPorts; i++) { for (i = 0; i < hub->descriptor->bNbrPorts; i++) {
ret = hub_port_status(hdev, i, &portstatus, &portchange); ret = hub_port_status(hdev, i, &portstatus, &portchange);
if (ret < 0) { if (ret < 0)
continue; continue;
} connect_change = 0;
if (portchange & USB_PORT_STAT_C_CONNECTION) { if (portchange & USB_PORT_STAT_C_CONNECTION) {
hub_port_connect_change(hub, i, portstatus, portchange); clear_port_feature(hdev,
} else if (portchange & USB_PORT_STAT_C_ENABLE) { i + 1, USB_PORT_FEAT_C_CONNECTION);
connect_change = 1;
}
if (portchange & USB_PORT_STAT_C_ENABLE) {
if (!connect_change)
dev_dbg (hub_dev, dev_dbg (hub_dev,
"port %d enable change, status %08x\n", "port %d enable change, "
"status %08x\n",
i + 1, portstatus); i + 1, portstatus);
clear_port_feature(hdev, clear_port_feature(hdev,
i + 1, USB_PORT_FEAT_C_ENABLE); i + 1, USB_PORT_FEAT_C_ENABLE);
...@@ -1696,15 +1712,14 @@ static void hub_events(void) ...@@ -1696,15 +1712,14 @@ static void hub_events(void)
* Works at least with mouse driver. * Works at least with mouse driver.
*/ */
if (!(portstatus & USB_PORT_STAT_ENABLE) if (!(portstatus & USB_PORT_STAT_ENABLE)
&& (portstatus & USB_PORT_STAT_CONNECTION) && !connect_change
&& (hdev->children[i])) { && hdev->children[i]) {
dev_err (hub_dev, dev_err (hub_dev,
"port %i " "port %i "
"disabled by hub (EMI?), " "disabled by hub (EMI?), "
"re-enabling...", "re-enabling...",
i + 1); i + 1);
hub_port_connect_change(hub, connect_change = 1;
i, portstatus, portchange);
} }
} }
...@@ -1732,6 +1747,10 @@ static void hub_events(void) ...@@ -1732,6 +1747,10 @@ static void hub_events(void)
clear_port_feature(hdev, clear_port_feature(hdev,
i + 1, USB_PORT_FEAT_C_RESET); i + 1, USB_PORT_FEAT_C_RESET);
} }
if (connect_change)
hub_port_connect_change(hub, i,
portstatus, portchange);
} /* end for i */ } /* end for i */
/* deal with hub status changes */ /* deal with hub status changes */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment