On Wed, 13 Feb 2002, Greg KH wrote:
> > Yes, I do totally agree with David: there is absolutely _no_ debounce
> > interval (connect-detect-to-port-reset) implemented in 2.4.18-pre9 hub.c
>
> Good catch. I'll send out a patch in a bit to fix this.
Me too - see below
> > struct usb_port_status - which becomes the transfer_buffer for the
> > usb_control_msg) call. Mapping stack locations for PCI-DMA however is
>
> Bleah, yes this should be fixed. Patches _gladly_ accepted for this
> problem.
Hi,
the attached patch below addresses both problems:
* usb_hub_port_debounce() provides the required debounce delay including
the "restart delay when spurious disconnect detected" behaviour
described in 7.1.7.1. However, I've used a minimum delay of 200ms
(instead TATTDB > 100ms) as this turned out to be needed to solve
the "not accepting new address" problem relyably for my usb-irda dongle.
I've kept the additional 400ms delay for low speed device as I wasn't
able to test whether this issue is still there.
Did some testing with both the usb-irda and the EZ-USB FX dev. board,
where the latter one turned out to be a pretty good trigger for connect/
disconnect races on hub-port, when one starts repeatedly pressing the
reset/disconnect button - Looks good for me now!
* usb_hub_port_status() was introduced to wrap the calls for
usb_get_port_status and uses a kmalloced temporary buffer instead
of exposing stack locations to PCI-DMA. Had it included during the
debounce tests and it work as expected for me.
Patch created and tested with 2.4.18-pre9.
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 01:49:34 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,49 @@
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, bogus_discon;
+ unsigned short portchange, portstatus;
+
+ bogus_discon = 0;
+ for (delay_time = 0; delay_time < HUB_DEBOUNCE_TIMEOUT; /* empty */ ) {
+
+ /* wait for debounce and to give the device stable buspower */
+
+ wait_ms(HUB_DEBOUNCE_STEP);
+
+ ret = usb_hub_port_status(hub, port, &portstatus, &portchange);
+ if (ret < 0)
+ return -1;
+
+ if (!(portstatus & USB_PORT_STAT_CONNECTION)) {
+ delay_time = 0;
+ if (++bogus_discon > 3) { /* 3 discon in a row are taken
+serious */
+ info("port %d: disconnect detected during
+connect-debounce", port+1);
+ return 1;
+ }
+ }
+ else {
+ delay_time += HUB_DEBOUNCE_STEP;
+ bogus_discon = 0;
+ }
+ }
+ return 0;
+}
+
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 +669,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 +805,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 +830,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