On Thu, 14 Feb 2002, Martin Diehl wrote:
> However, I think there is something which could be done better: currently
> the portstatus is sampled every 10 ms and the connect _status_ is what we
> look at. Hence we might miss short transient disconnect/reconnect changes
> between the sample. I'm going to change this to use the port connect
> _change_ information.
Updated patch below. Using the status change information means also to
get rid of the 3-times-disconnect-are-real test, since a real disconnect
wouldn't produce any more status changes - hence we are guaranteed to
timeout whatever happens.
The patch also fixes a typo in the kmalloc-sizeof, which was noticed by
Dmitri - furtunately sizeof(some *)>=sizeof(struct usb_port_status) ;-)
rediffed patch replaces the former one.
Martin
----------------------
--- linux-2.4.18-pre9/drivers/usb/hub.c Wed Nov 28 15:16:48 2001
+++ v2.4.18-pre9-md/drivers/usb/hub.c Thu Feb 14 10:42:50 2002
@@ -496,6 +496,29 @@
err("cannot disconnect hub %d", dev->devnum);
}
+static int usb_hub_port_status(struct usb_device *hub, int port,
+ unsigned short *status, unsigned short *change)
+{
+ struct usb_port_status *portsts;
+ int ret = -1;
+
+ portsts = kmalloc(sizeof(*portsts), GFP_KERNEL);
+ if (portsts) {
+ ret = usb_get_port_status(hub, port + 1, portsts);
+ if (ret < 0)
+ err("get_port_status(%d) failed (err = %d)", port + 1, ret);
+ else {
+ *status = le16_to_cpu(portsts->wPortStatus);
+ *change = le16_to_cpu(portsts->wPortChange);
+ dbg("port %d, portstatus %x, change %x, %s", port + 1,
+ *status, *change, portspeed(*status));
+ ret = 0;
+ }
+ kfree(portsts);
+ }
+ return ret;
+}
+
#define HUB_RESET_TRIES 5
#define HUB_PROBE_TRIES 2
#define HUB_SHORT_RESET_TIME 10
@@ -507,7 +530,6 @@
struct usb_device *dev, unsigned int delay)
{
int delay_time, ret;
- struct usb_port_status portsts;
unsigned short portchange, portstatus;
for (delay_time = 0; delay_time < HUB_RESET_TIMEOUT; delay_time += delay) {
@@ -515,17 +537,11 @@
wait_ms(delay);
/* read and decode port status */
- ret = usb_get_port_status(hub, port + 1, &portsts);
+ ret = usb_hub_port_status(hub, port, &portstatus, &portchange);
if (ret < 0) {
- err("get_port_status(%d) failed (err = %d)", port + 1, ret);
return -1;
}
- portstatus = le16_to_cpu(portsts.wPortStatus);
- portchange = le16_to_cpu(portsts.wPortChange);
- dbg("port %d, portstatus %x, change %x, %s", port + 1,
- portstatus, portchange, portspeed (portstatus));
-
/* bomb out completely if something weird happened */
if ((portchange & USB_PORT_STAT_C_CONNECTION))
return -1;
@@ -592,17 +608,43 @@
port + 1, hub->devnum, ret);
}
+#define HUB_DEBOUNCE_TIMEOUT 200 /* 7.1.7.1: min 100ms - restart if disconnect
+*/
+#define HUB_DEBOUNCE_STEP 10 /* interval when to check for spurious
+disconnect */
+
+/* return: -1 on error, 0 on success, 1 on disconnect. */
+static int usb_hub_port_debounce(struct usb_device *hub, int port)
+{
+ int delay_time, ret;
+ unsigned short portchange, portstatus;
+
+ for (delay_time = 0; delay_time < HUB_DEBOUNCE_TIMEOUT; /* empty */ ) {
+
+ /* wait for debounce and for device getting stable buspower */
+
+ wait_ms(HUB_DEBOUNCE_STEP);
+
+ ret = usb_hub_port_status(hub, port, &portstatus, &portchange);
+ if (ret < 0)
+ return -1;
+
+ if ((portchange & USB_PORT_STAT_C_CONNECTION)) {
+ usb_clear_port_feature(hub, port+1,
+USB_PORT_FEAT_C_CONNECTION);
+ delay_time = 0;
+ }
+ else
+ delay_time += HUB_DEBOUNCE_STEP;
+ }
+ return ((portstatus&USB_PORT_STAT_CONNECTION)) ? 0 : 1;
+}
+
static void usb_hub_port_connect_change(struct usb_device *hub, int port,
- struct usb_port_status *portsts)
+ unsigned char portstatus, unsigned char portchange)
{
struct usb_device *dev;
- unsigned short portstatus, portchange;
unsigned int delay = HUB_SHORT_RESET_TIME;
int i;
char *portstr, *tempstr;
- portstatus = le16_to_cpu(portsts->wPortStatus);
- portchange = le16_to_cpu(portsts->wPortChange);
dbg("port %d, portstatus %x, change %x, %s",
port + 1, portstatus, portchange, portspeed (portstatus));
@@ -621,6 +663,12 @@
return;
}
+ if (usb_hub_port_debounce(hub, port)) {
+ err("connect-debounce failed, port %d disabled", port+1);
+ usb_hub_port_disable(hub, port);
+ return;
+ }
+
/* Some low speed devices have problems with the quick delay, so */
/* be a bit pessimistic with those devices. RHbug #23670 */
if (portstatus & USB_PORT_STAT_LOW_SPEED) {
@@ -751,22 +799,18 @@
}
for (i = 0; i < hub->descriptor->bNbrPorts; i++) {
- struct usb_port_status portsts;
unsigned short portstatus, portchange;
- ret = usb_get_port_status(dev, i + 1, &portsts);
+ ret = usb_hub_port_status(dev, i, &portstatus, &portchange);
if (ret < 0) {
err("get_port_status failed (err = %d)", ret);
continue;
}
- portstatus = le16_to_cpu(portsts.wPortStatus);
- portchange = le16_to_cpu(portsts.wPortChange);
-
if (portchange & USB_PORT_STAT_C_CONNECTION) {
dbg("port %d connection change", i + 1);
- usb_hub_port_connect_change(dev, i, &portsts);
+ usb_hub_port_connect_change(dev, i, portstatus,
+portchange);
} else if (portchange & USB_PORT_STAT_C_ENABLE) {
dbg("port %d enable change, status %x", i + 1,
portstatus);
usb_clear_port_feature(dev, i + 1,
USB_PORT_FEAT_C_ENABLE);
@@ -780,7 +824,7 @@
(portstatus & USB_PORT_STAT_CONNECTION) &&
(dev->children[i])) {
err("already running port %i disabled by hub
(EMI?), re-enabling...",
i + 1);
- usb_hub_port_connect_change(dev, i, &portsts);
+ usb_hub_port_connect_change(dev, i,
+portstatus, portchange);
}
}
_______________________________________________
[EMAIL PROTECTED]
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel